From adfa77822ab606485daf8ade3c978998119fcd62 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Fri, 12 Mar 2021 14:44:04 +0100 Subject: [PATCH 001/290] Initial internal and external redefinition APIs --- .../espresso/jdwp/api/JDWPContext.java | 2 +- .../truffle/espresso/jdwp/api/MethodHook.java | 41 +++++ .../truffle/espresso/jdwp/api/MethodRef.java | 8 +- .../espresso/jdwp/api/MethodVariable.java | 41 +++++ .../truffle/espresso/jdwp/api/VMListener.java | 23 ++- .../jdwp/impl/DebuggerConnection.java | 1 + .../espresso/jdwp/impl/EmptyListener.java | 5 + .../truffle/espresso/jdwp/impl/JDWP.java | 6 +- .../jdwp/impl/MethodBreakpointInfo.java | 31 ++-- .../jdwp/impl/RequestedJDWPEvents.java | 4 +- .../jdwp/impl/VMEventListenerImpl.java | 71 +++++++- .../espresso/hotswap/EspressoHotSwap.java | 71 ++++++++ .../espresso/hotswap/HotSwapAction.java | 45 +++++ .../espresso/hotswap/HotSwapHandler.java | 109 ++++++++++++ .../espresso/hotswap/HotSwapPlugin.java | 47 +++++ ...ion.plugins.api.InternalRedefinitionPlugin | 2 + .../espresso/impl/ClassRegistries.java | 17 +- .../truffle/espresso/impl/ClassRegistry.java | 2 + .../espresso/impl/ConstantPoolPatcher.java | 1 + .../truffle/espresso/impl/LinkedKlass.java | 2 +- .../oracle/truffle/espresso/impl/Method.java | 65 +++---- .../truffle/espresso/impl/ObjectKlass.java | 8 + .../truffle/espresso/impl/ParserKlass.java | 6 +- .../truffle/espresso/nodes/BytecodeNode.java | 15 +- .../nodes/interop/InvokeEspressoNode.java | 2 +- .../nodes/methodhandle/MHLinkToNode.java | 2 +- .../quick/invoke/InvokeInterfaceNode.java | 2 +- .../nodes/quick/invoke/InvokeSpecialNode.java | 2 +- .../nodes/quick/invoke/InvokeStaticNode.java | 2 +- .../nodes/quick/invoke/InvokeVirtualNode.java | 2 +- .../{impl => redefinition}/ChangePacket.java | 6 +- .../{impl => redefinition}/ClassInfo.java | 9 +- .../redefinition/ClassLoadListener.java | 7 + .../ClassRedefinition.java | 60 +++++-- .../DefineKlassListener.java | 4 +- .../DetectedChange.java | 25 ++- .../HotSwapClassInfo.java | 3 +- .../ImmutableClassInfo.java | 3 +- .../InnerClassRedefiner.java | 50 +++--- .../RedefintionNotSupportedException.java | 8 +- .../plugins/api/ClassLoadAction.java} | 10 +- .../api/InternalRedefinitionPlugin.java | 123 +++++++++++++ .../plugins/api/MethodEntryHook.java | 30 ++++ .../plugins/api/MethodExitHook.java | 29 +++ .../plugins/api/MethodLocator.java | 41 +++++ .../plugins/api/RedefineObject.java | 39 +++++ .../plugins/api/RedefinitionAction.java | 7 + .../plugins/api/RedefintionHook.java | 67 +++++++ .../plugins/api/TriggerClass.java | 50 ++++++ .../plugins/api/TriggerClassHook.java | 29 +++ .../plugins/impl/CachedRedefineObject.java | 130 ++++++++++++++ .../plugins/impl/ExternalPluginHandler.java | 78 +++++++++ .../plugins/impl/RedefineListener.java | 36 ++++ .../plugins/impl/RedefineObjectImpl.java | 138 +++++++++++++++ .../impl/RedefinitionPluginHandler.java | 165 ++++++++++++++++++ .../plugins/impl/UncachedRedefineObject.java | 67 +++++++ .../jdkproxy/JDKProxyRedefinitionPlugin.java | 159 +++++++++++++++++ .../plugins/micronaut/MicronautPlugin.java | 136 +++++++++++++++ .../espresso/runtime/EspressoContext.java | 13 +- .../espresso/runtime/JDWPContextImpl.java | 141 +++++++++------ ...uffle_espresso_hotswap_HotSwapHandler.java | 40 +++++ ..._sun_reflect_NativeMethodAccessorImpl.java | 2 +- 62 files changed, 2148 insertions(+), 192 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVariable.java create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/ChangePacket.java (91%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/ClassInfo.java (95%) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/ClassRedefinition.java (92%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/DefineKlassListener.java (91%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/DetectedChange.java (76%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/HotSwapClassInfo.java (98%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/ImmutableClassInfo.java (97%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/InnerClassRedefiner.java (91%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/RedefintionNotSupportedException.java (83%) rename espresso/src/{com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodBreakpoint.java => com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/ClassLoadAction.java} (80%) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java 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 49e8852444da..feb7d7493361 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 @@ -466,7 +466,7 @@ public interface JDWPContext { * @param redefineInfos the information about the original class and the new class bytes * @return 0 on success or the appropriate {@link ErrorCodes} if an error occur */ - int redefineClasses(RedefineInfo[] redefineInfos); + int redefineClasses(List redefineInfos); /** * Exit all monitors that was entered by the frame. diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java new file mode 100644 index 000000000000..d7420f5ed6d9 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020, 2021, 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 MethodHook { + + enum Kind { + ONE_TIME, + INDEFINITE + } + + default int getRequestId() { + return -1; + } + + Kind getKind(); + + boolean onMethodEnter(MethodRef method, MethodVariable[] variables); + + boolean onMethodExit(MethodRef method, Object returnValue); +} 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 1e86414e7ce0..f2cdcce9172a 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 @@ -179,28 +179,28 @@ public interface MethodRef { * * @return array of method breakpoint info */ - MethodBreakpoint[] getMethodBreakpointInfos(); + MethodHook[] getMethodHooks(); /** * Add a new method breakpoint with the given info on this method. * * @param info the info that describes the breakpoint */ - void addMethodBreakpointInfo(MethodBreakpoint info); + void addMethodHook(MethodHook info); /** * Remove a method breakpoint with the given info on this method. * * @param requestId the ID for the request that set the breakpoint */ - void removeMethodBreakpointInfo(int requestId); + void removedMethodHook(int requestId); /** * Determines if there are any breakpoints set on this method. * * @return true if this method has any breakpoints, false otherwise */ - boolean hasActiveBreakpoint(); + boolean hasActiveHook(); /** * Determine if this method is obsolete. A method is obsolete if it has been replaced by a diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVariable.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVariable.java new file mode 100644 index 000000000000..394b8d31a05b --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVariable.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, 2021, 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 final class MethodVariable { + private final String identifier; + private final Object value; + + public MethodVariable(String identifier, Object value) { + this.identifier = identifier; + this.value = value; + } + + public String getIdentifier() { + return identifier; + } + + public Object getValue() { + return value; + } +} 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 064cde2934a4..5adc987ad0ba 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 @@ -70,12 +70,25 @@ public interface VMListener { boolean onFieldAccess(FieldRef field, Object receiver); /** - * This method will be called when a method is about to return. If will determine if there is an - * active method exit breakpoint that will be triggered. + * This method will be called when a method is entered iff there is an active + * {@link MethodHook}. Returns true if the method has an active method exit breakpoint that + * should be triggered. * - * @param method the field - * @param returnValue owner of the field - * @return true if a breakpoint should be hit due to the modification + * @param method the method + * @param scope the {@link com.oracle.truffle.api.interop.InteropLibrary} object representing + * local variables in scope + * @return true a breakpoint should be hit on method entry + */ + boolean onMethodEntry(MethodRef method, Object scope); + + /** + * This method will be called when a method is about to return iff there is an active + * {@link MethodHook}. Returns true if the method has an active method exit breakpoint that + * should be triggered. + * + * @param method the method + * @param returnValue the return value + * @return true if a breakpoint should be hit on method exit */ boolean onMethodReturn(MethodRef method, Object returnValue); diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java index 7f5cd8f93f06..a1391500f920 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java @@ -607,6 +607,7 @@ private void processPacket(Packet packet) { JDWPLogger.throwing(JDWPLogger.LogLevel.ALL, t); PacketStream reply = new PacketStream().replyPacket().id(packet.id); reply.errorCode(ErrorCodes.INTERNAL); + t.printStackTrace(); handleReply(packet, new CommandResult(reply)); } finally { if (entered) { diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/EmptyListener.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/EmptyListener.java index 58479a3e8a3e..ddc38d59b30d 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/EmptyListener.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/EmptyListener.java @@ -59,6 +59,11 @@ public boolean onFieldAccess(FieldRef field, Object receiver) { return false; } + @Override + public boolean onMethodEntry(MethodRef method, Object scope) { + return false; + } + @Override public boolean onMethodReturn(MethodRef method, Object returnValue) { return false; 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 939eff0829c5..c49979e4bd3b 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 @@ -387,7 +387,7 @@ static CommandResult createReply(Packet packet, JDWPContext context) { PacketStream reply = new PacketStream().replyPacket().id(packet.id); int classes = input.readInt(); JDWPLogger.log("Request to redefine %d classes received", JDWPLogger.LogLevel.REDEFINE, classes); - RedefineInfo[] redefineInfos = new RedefineInfo[classes]; + List redefineInfos = new ArrayList<>(classes); for (int i = 0; i < classes; i++) { KlassRef klass = null; long refTypeId = input.readLong(); @@ -408,7 +408,7 @@ static CommandResult createReply(Packet packet, JDWPContext context) { int byteLength = input.readInt(); byte[] classBytes = input.readByteArray(byteLength); - redefineInfos[i] = new RedefineInfo(klass, classBytes); + redefineInfos.add(new RedefineInfo(klass, classBytes)); } int errorCode = context.redefineClasses(redefineInfos); @@ -2990,6 +2990,8 @@ private static void writeMethodResult(PacketStream reply, JDWPContext context, T JDWPLogger.log("Internal Espresso error: %s", JDWPLogger.LogLevel.ALL, t); JDWPLogger.throwing(JDWPLogger.LogLevel.ALL, t); reply.errorCode(ErrorCodes.INTERNAL); + System.out.println("ERROR!"); + t.printStackTrace(); } } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/MethodBreakpointInfo.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/MethodBreakpointInfo.java index fa6c813b0576..d842b174dc48 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/MethodBreakpointInfo.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/MethodBreakpointInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, 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 @@ -22,12 +22,13 @@ */ package com.oracle.truffle.espresso.jdwp.impl; -import com.oracle.truffle.espresso.jdwp.api.MethodBreakpoint; -import com.oracle.truffle.espresso.jdwp.api.MethodRef; - import java.util.Arrays; -public class MethodBreakpointInfo extends AbstractBreakpointInfo implements MethodBreakpoint { +import com.oracle.truffle.espresso.jdwp.api.MethodHook; +import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVariable; + +public final class MethodBreakpointInfo extends AbstractBreakpointInfo implements MethodHook { private MethodRef[] methods = new MethodRef[0]; @@ -35,11 +36,6 @@ public MethodBreakpointInfo(RequestFilter filter) { super(filter); } - @Override - public boolean isLineBreakpoint() { - return true; - } - public void addMethod(MethodRef method) { methods = Arrays.copyOf(methods, methods.length + 1); methods[methods.length - 1] = method; @@ -48,4 +44,19 @@ public void addMethod(MethodRef method) { public MethodRef[] getMethods() { return methods; } + + @Override + public Kind getKind() { + return Kind.INDEFINITE; + } + + @Override + public boolean onMethodEnter(@SuppressWarnings("unused") MethodRef method, @SuppressWarnings("unused") MethodVariable[] variables) { + return true; + } + + @Override + public boolean onMethodExit(@SuppressWarnings("unused") MethodRef method, @SuppressWarnings("unused") Object returnValue) { + return true; + } } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/RequestedJDWPEvents.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/RequestedJDWPEvents.java index 68b2acbaa63f..f0d8b336dd18 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/RequestedJDWPEvents.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/RequestedJDWPEvents.java @@ -111,7 +111,7 @@ public CommandResult registerEvent(Packet packet, Commands callback) { eventListener.addBreakpointRequest(filter.getRequestId(), methodInfo); for (KlassRef klass : filter.getKlassRefPatterns()) { for (MethodRef method : klass.getDeclaredMethodRefs()) { - method.addMethodBreakpointInfo(methodInfo); + method.addMethodHook(methodInfo); methodInfo.addMethod(method); } } @@ -334,7 +334,7 @@ public CommandResult clearRequest(Packet packet) { case METHOD_EXIT: MethodBreakpointInfo methodInfo = (MethodBreakpointInfo) requestFilter.getBreakpointInfo(); for (MethodRef method : methodInfo.getMethods()) { - method.removeMethodBreakpointInfo(requestFilter.getRequestId()); + method.removedMethodHook(requestFilter.getRequestId()); } break; case BREAKPOINT: diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java index b4b874e81d1a..c8a964b38d39 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java @@ -33,20 +33,27 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.debug.Breakpoint; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.espresso.jdwp.api.CallFrame; import com.oracle.truffle.espresso.jdwp.api.FieldBreakpoint; import com.oracle.truffle.espresso.jdwp.api.FieldRef; import com.oracle.truffle.espresso.jdwp.api.Ids; import com.oracle.truffle.espresso.jdwp.api.JDWPContext; import com.oracle.truffle.espresso.jdwp.api.KlassRef; -import com.oracle.truffle.espresso.jdwp.api.MethodBreakpoint; +import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVariable; import com.oracle.truffle.espresso.jdwp.api.TagConstants; import sun.reflect.generics.reflectiveObjects.NotImplementedException; public final class VMEventListenerImpl implements VMEventListener { + public static final InteropLibrary UNCACHED = InteropLibrary.getUncached(); + private final Ids ids; private final JDWPContext context; private final DebuggerController debuggerController; @@ -143,15 +150,67 @@ public boolean onFieldAccess(FieldRef field, Object receiver) { return active; } + @Override + @TruffleBoundary + public boolean onMethodEntry(MethodRef method, Object scope) { + boolean active = false; + // collect variable information from scope + List variables = new ArrayList<>(1); + try { + if (UNCACHED.hasMembers(scope)) { + Object identifiers = UNCACHED.getMembers(scope); + if (UNCACHED.hasArrayElements(identifiers)) { + long size = UNCACHED.getArraySize(identifiers); + for (long i = 0; i < size; i++) { + String identifier = (String) UNCACHED.readArrayElement(identifiers, i); + Object value = UNCACHED.readMember(scope, identifier); + variables.add(new MethodVariable(identifier, value)); + } + } + } + } catch (UnsupportedMessageException | InvalidArrayIndexException | UnknownIdentifierException e) { + // not able to fetch locals, so leave variables list empty + } + + for (MethodHook hook : method.getMethodHooks()) { + // pass on the variables to the method entry hook + if (hook.onMethodEnter(method, variables.toArray(new MethodVariable[variables.size()]))) { + // OK, tell the Debug API to suspend the thread now + debuggerController.prepareMethodBreakpoint(new MethodBreakpointEvent((MethodBreakpointInfo) hook, null)); + debuggerController.suspend(context.asGuestThread(Thread.currentThread())); + active = true; + } + switch (hook.getKind()) { + case ONE_TIME: + method.removedMethodHook(-1); + break; + case INDEFINITE: + // leave the hook active + break; + } + } + return active; + } + @Override @TruffleBoundary public boolean onMethodReturn(MethodRef method, Object returnValue) { boolean active = false; - for (MethodBreakpoint info : method.getMethodBreakpointInfos()) { - // OK, tell the Debug API to suspend the thread now - debuggerController.prepareMethodBreakpoint(new MethodBreakpointEvent((MethodBreakpointInfo) info, returnValue)); - debuggerController.suspend(context.asGuestThread(Thread.currentThread())); - active = true; + for (MethodHook hook : method.getMethodHooks()) { + if (hook.onMethodExit(method, returnValue)) { + // OK, tell the Debug API to suspend the thread now + debuggerController.prepareMethodBreakpoint(new MethodBreakpointEvent((MethodBreakpointInfo) hook, returnValue)); + debuggerController.suspend(context.asGuestThread(Thread.currentThread())); + active = true; + } + switch (hook.getKind()) { + case ONE_TIME: + method.removedMethodHook(-1); + break; + case INDEFINITE: + // leave the hook active + break; + } } return active; } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java new file mode 100644 index 000000000000..c8f11f5f23c3 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +@SuppressWarnings("unused") +public final class EspressoHotSwap { + + private EspressoHotSwap() { + throw new RuntimeException("No instance of EspressoHotSwap can be created"); + } + + private static final HotSwapHandler handler = HotSwapHandler.create(); + + public static void registerPlugin(HotSwapPlugin plugin) { + if (handler != null) { + handler.addPlugin(plugin); + } else { + // TODO - should we log that plugin registration is only available on supported Espresso VM? + } + } + + public static void registerHotSwapAction(Class klass, HotSwapAction action) { + if (handler != null) { + handler.registerHotSwapAction(klass, action); + } + } + + public static void registerClassInitHotSwap(Class klass, boolean onChange) { + if (handler != null) { + handler.registerStaticClassInitHotSwap(klass, onChange); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java new file mode 100644 index 000000000000..1e43bd871e68 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +public interface HotSwapAction { + void onHotSwap(); +} diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java new file mode 100644 index 000000000000..4a4bee7ff740 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +final class HotSwapHandler { + + private static HotSwapHandler theHandler; + + private final Set plugins = Collections.synchronizedSet(new HashSet<>()); + private final Map, Set> hotSwapActions = new HashMap<>(); + private final Map, Boolean> staticInitializerHotSwap = new HashMap<>(); + + private HotSwapHandler() { + } + + static HotSwapHandler create() { + theHandler = new HotSwapHandler(); + // register handler to Espresso if present + return registerHandler(theHandler) ? theHandler : null; + } + + // substituted by Espresso + private static boolean registerHandler(@SuppressWarnings("unused") Object handler) { + return false; + } + + void addPlugin(HotSwapPlugin plugin) { + plugins.add(plugin); + } + + public void registerHotSwapAction(Class klass, HotSwapAction action) { + hotSwapActions.putIfAbsent(klass, new HashSet<>()); + hotSwapActions.get(klass).add(action); + } + + public void registerStaticClassInitHotSwap(Class klass, boolean onChange) { + if (!staticInitializerHotSwap.containsKey(klass)) { + staticInitializerHotSwap.put(klass, onChange); + } else if (!onChange) { + staticInitializerHotSwap.put(klass, false); + } + } + + @SuppressWarnings("unused") + public void postHotSwap(Class[] changedClasses) { + // fire all registered HotSwap actions + for (Class klass : changedClasses) { + Set actions = hotSwapActions.getOrDefault(klass, Collections.emptySet()); + actions.forEach(HotSwapAction::onHotSwap); + } + // fire a generic HotSwap plugin listener + for (HotSwapPlugin plugin : plugins) { + plugin.postHotSwap(); + } + } + + @SuppressWarnings("unused") + public boolean rerunClassInit(Class klass, boolean changed) { + if (staticInitializerHotSwap.containsKey(klass)) { + boolean onlyOnChange = staticInitializerHotSwap.get(klass); + return !onlyOnChange || changed; + } + return false; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java new file mode 100644 index 000000000000..edfd48170d98 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +public interface HotSwapPlugin { + String getName(); + + void postHotSwap(); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin new file mode 100644 index 000000000000..a03c21cb2019 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin @@ -0,0 +1,2 @@ +com.oracle.truffle.espresso.redefinition.plugins.micronaut.MicronautPlugin +com.oracle.truffle.espresso.redefinition.plugins.jdkproxy.JDKProxyRedefinitionPlugin diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java index 20b1c79c0acf..a31ee895f5cb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java @@ -39,6 +39,7 @@ import com.oracle.truffle.espresso.descriptors.Symbol.Type; import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.ClassLoadListener; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; import com.oracle.truffle.espresso.substitutions.Host; @@ -61,6 +62,8 @@ public final class ClassRegistries { // specify it as volatile. private int totalClassLoadersSet = 0; + private ClassLoadListener classLoadListener; + public ClassRegistries(EspressoContext context) { this.context = context; this.bootClassRegistry = new BootClassRegistry(context); @@ -231,14 +234,14 @@ public void checkLoadingConstraint(Symbol type, StaticObject loader1, Stat } } - void recordConstraint(Symbol type, Klass klass, StaticObject loader) { + public void recordConstraint(Symbol type, Klass klass, StaticObject loader) { assert !Types.isArray(type); if (!Types.isPrimitive(type)) { constraints.recordConstraint(type, klass, loader); } } - void removeUnloadedKlassConstraint(Klass klass, Symbol type) { + public void removeUnloadedKlassConstraint(Klass klass, Symbol type) { assert klass.isInstanceClass(); constraints.removeUnloadedKlassConstraint(klass, type); } @@ -298,6 +301,16 @@ int[] aliveLoaders() { return loaders; } + public void registerListener(ClassLoadListener listener) { + this.classLoadListener = listener; + } + + public void onKlassDefined(ObjectKlass klass) { + if (classLoadListener != null) { + classLoadListener.onClassLoad(klass); + } + } + static class RegistryEntry { private final Klass klass; private volatile Set domains = null; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java index 3fd7dfd2e485..7bd6496c3b17 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java @@ -40,6 +40,7 @@ import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.perf.DebugCloseable; import com.oracle.truffle.espresso.perf.DebugTimer; +import com.oracle.truffle.espresso.redefinition.DefineKlassListener; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; import com.oracle.truffle.espresso.runtime.StaticObject; @@ -346,6 +347,7 @@ private ObjectKlass createAndPutKlass(Meta meta, ParserKlass parserKlass, Symbol EspressoError.guarantee(previous == null, "Class " + type + " is already defined"); getRegistries().recordConstraint(type, klass, getClassLoader()); + getRegistries().onKlassDefined(klass); if (defineKlassListener != null) { defineKlassListener.onKlassDefined(klass); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ConstantPoolPatcher.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ConstantPoolPatcher.java index 837ced3f71f5..5d9d1c09751a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ConstantPoolPatcher.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ConstantPoolPatcher.java @@ -26,6 +26,7 @@ import com.oracle.truffle.espresso.classfile.ConstantPool; import com.oracle.truffle.espresso.descriptors.ByteSequence; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.redefinition.InnerClassRedefiner; import com.oracle.truffle.espresso.runtime.EspressoContext; import java.lang.instrument.IllegalClassFormatException; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java index 4e5d89d23f62..5270bde17c6a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java @@ -131,7 +131,7 @@ Symbol getName() { return parserKlass.getName(); } - ParserKlass getParserKlass() { + public ParserKlass getParserKlass() { return parserKlass; } 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 063c1dcb02ad..5e22cca89549 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 @@ -80,7 +80,7 @@ import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.LineNumberTableRef; import com.oracle.truffle.espresso.jdwp.api.LocalVariableTableRef; -import com.oracle.truffle.espresso.jdwp.api.MethodBreakpoint; +import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; import com.oracle.truffle.espresso.jni.Mangle; import com.oracle.truffle.espresso.meta.EspressoError; @@ -92,6 +92,7 @@ import com.oracle.truffle.espresso.nodes.EspressoRootNode; import com.oracle.truffle.espresso.nodes.NativeMethodNode; import com.oracle.truffle.espresso.nodes.methodhandle.MethodHandleIntrinsicNode; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.runtime.Attribute; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics; @@ -993,49 +994,49 @@ public String getGenericSignatureAsString() { return genericSignature; } - private final Field.StableBoolean hasActiveBreakpoints = new Field.StableBoolean(false); + private final Field.StableBoolean hasActiveHook = new Field.StableBoolean(false); - private MethodBreakpoint[] infos = new MethodBreakpoint[0]; + private MethodHook[] hooks = new MethodHook[0]; - public boolean hasActiveBreakpoint() { - return hasActiveBreakpoints.get(); + public boolean hasActiveHook() { + return hasActiveHook.get(); } - public MethodBreakpoint[] getMethodBreakpointInfos() { - return infos; + public MethodHook[] getMethodHooks() { + return hooks; } - public void addMethodBreakpointInfo(MethodBreakpoint info) { - hasActiveBreakpoints.set(true); - if (infos.length == 0) { - infos = new MethodBreakpoint[]{info}; + public void addMethodHook(MethodHook info) { + hasActiveHook.set(true); + if (hooks.length == 0) { + hooks = new MethodHook[]{info}; return; } - infos = Arrays.copyOf(infos, infos.length + 1); - infos[infos.length - 1] = info; + hooks = Arrays.copyOf(hooks, hooks.length + 1); + hooks[hooks.length - 1] = info; } - public void removeMethodBreakpointInfo(int requestId) { + public void removeActiveHook(int requestId) { // shrink the array to avoid null values - if (infos.length == 0) { - throw new RuntimeException("Method: " + getNameAsString() + " should contain method breakpoint info"); - } else if (infos.length == 1) { - infos = new MethodBreakpoint[0]; - hasActiveBreakpoints.set(false); + if (hooks.length == 0) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); + } else if (hooks.length == 1) { + hooks = new MethodHook[0]; + hasActiveHook.set(false); } else { int removeIndex = -1; - for (int i = 0; i < infos.length; i++) { - if (infos[i].getRequestId() == requestId) { + for (int i = 0; i < hooks.length; i++) { + if (hooks[i].getRequestId() == requestId) { removeIndex = i; break; } } - MethodBreakpoint[] temp = new MethodBreakpoint[infos.length - 1]; + MethodHook[] temp = new MethodHook[hooks.length - 1]; for (int i = 0; i < temp.length; i++) { - temp[i] = i < removeIndex ? infos[i] : infos[i + 1]; + temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; } - infos = temp; + hooks = temp; } } @@ -1366,23 +1367,23 @@ public int getLastLine() { } @Override - public MethodBreakpoint[] getMethodBreakpointInfos() { - return getMethod().getMethodBreakpointInfos(); + public MethodHook[] getMethodHooks() { + return getMethod().getMethodHooks(); } @Override - public void addMethodBreakpointInfo(MethodBreakpoint info) { - getMethod().addMethodBreakpointInfo(info); + public void addMethodHook(MethodHook info) { + getMethod().addMethodHook(info); } @Override - public void removeMethodBreakpointInfo(int requestId) { - getMethod().removeMethodBreakpointInfo(requestId); + public void removedMethodHook(int requestId) { + getMethod().removeActiveHook(requestId); } @Override - public boolean hasActiveBreakpoint() { - return getMethod().hasActiveBreakpoint(); + public boolean hasActiveHook() { + return getMethod().hasActiveHook(); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index 30dfec5ebb4d..302cf011d838 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -68,6 +68,9 @@ import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.ChangePacket; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.DetectedChange; import com.oracle.truffle.espresso.runtime.Attribute; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; @@ -1197,6 +1200,11 @@ public void redefineClass(ChangePacket packet, List refreshSubClass oldVersion.assumption.invalidate(); } + // used by some plugins during klass redefitnion + public void reRunClinit() { + getClassInitializer().getCallTarget().call(); + } + private static void checkCopyMethods(Method method, Method[][] table, Method.SharedRedefinitionContent content, Ids ids) { for (Method[] methods : table) { checkCopyMethods(method, methods, content, ids); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserKlass.java index cd905632bdb6..07015a0c8d7d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserKlass.java @@ -114,15 +114,15 @@ public ConstantPool getConstantPool() { return pool; } - ParserMethod[] getMethods() { + public ParserMethod[] getMethods() { return methods; } - ParserField[] getFields() { + public ParserField[] getFields() { return fields; } - Attribute getAttribute(Symbol attributeName) { + public Attribute getAttribute(Symbol attributeName) { for (Attribute attribute : attributes) { if (attributeName.equals(attribute.getName())) { return attribute; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 624c2dddff83..84f5efad1d18 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -656,7 +656,7 @@ Object executeBody(VirtualFrame frame) { setBCI(frame, curBCI); if (instrument != null) { - instrument.notifyEntry(frame); + instrument.notifyEntry(frame, this); } onStart(primitives, refs); @@ -2540,15 +2540,16 @@ void notifyStatement(VirtualFrame frame, int statementIndex, int nextStatementIn enterAt(frame, nextStatementIndex); } - public void notifyEntry(@SuppressWarnings("unused") VirtualFrame frame) { - // TODO(Gregersen) - method entry breakpoints are currently implemented by submitting - // first line breakpoints within each method. This works insofar the method has a valid - // line table. For classes compiled without debug information we could use this hook - // instead. + public void notifyEntry(@SuppressWarnings("unused") VirtualFrame frame, EspressoInstrumentableNode instrumentableNode) { + if (method.hasActiveHook()) { + if (context.getJDWPListener().onMethodEntry(method, instrumentableNode.getScope(frame, true))) { + enterAt(frame, 0); + } + } } public void notifyReturn(VirtualFrame frame, int statementIndex, Object returnValue) { - if (method.hasActiveBreakpoint()) { + if (method.hasActiveHook()) { if (context.getJDWPListener().onMethodReturn(method, returnValue)) { enterAt(frame, statementIndex); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java index e31180e2cd44..25c080c57d3f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java @@ -34,7 +34,7 @@ import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.meta.EspressoError; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/methodhandle/MHLinkToNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/methodhandle/MHLinkToNode.java index 84e123642151..a8b2e3afc941 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/methodhandle/MHLinkToNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/methodhandle/MHLinkToNode.java @@ -35,7 +35,7 @@ import com.oracle.truffle.espresso.descriptors.Signatures; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Type; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Method; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java index 4eb2170b8541..626cc22c560f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java @@ -29,7 +29,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java index 01bdcff92c9a..1e264fe811d0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java @@ -27,7 +27,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.nodes.BytecodeNode; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java index c159f11e8ba2..b95fc83d6350 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java @@ -28,7 +28,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; import com.oracle.truffle.espresso.descriptors.Symbol.Name; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.nodes.BytecodeNode; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java index bfd9832f9a44..5d4b81e03e27 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java @@ -29,7 +29,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ChangePacket.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ChangePacket.java similarity index 91% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ChangePacket.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ChangePacket.java index 849d6f965d80..5087b34525d1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ChangePacket.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ChangePacket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2021, 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 @@ -20,7 +20,9 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; + +import com.oracle.truffle.espresso.impl.ParserKlass; public final class ChangePacket { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassInfo.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassInfo.java similarity index 95% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassInfo.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassInfo.java index e3d6544b2fc0..5d3c32ca172b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassInfo.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassInfo.java @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import com.oracle.truffle.espresso.classfile.ClassfileParser; import com.oracle.truffle.espresso.classfile.ClassfileStream; @@ -28,6 +28,13 @@ import com.oracle.truffle.espresso.classfile.attributes.EnclosingMethodAttribute; import com.oracle.truffle.espresso.classfile.constantpool.NameAndTypeConstant; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.impl.ParserField; +import com.oracle.truffle.espresso.impl.ParserKlass; +import com.oracle.truffle.espresso.impl.ParserMethod; import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java new file mode 100644 index 000000000000..c22bbbcff6d9 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java @@ -0,0 +1,7 @@ +package com.oracle.truffle.espresso.redefinition; + +import com.oracle.truffle.espresso.impl.ObjectKlass; + +public interface ClassLoadListener { + void onClassLoad(ObjectKlass klass); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java similarity index 92% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRedefinition.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index b376d460c97b..7957b7ff0587 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import java.util.ArrayList; import java.util.Arrays; @@ -46,10 +46,20 @@ import com.oracle.truffle.espresso.classfile.attributes.LocalVariableTable; import com.oracle.truffle.espresso.classfile.constantpool.PoolConstant; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ClassRegistry; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.impl.ParserField; +import com.oracle.truffle.espresso.impl.ParserKlass; +import com.oracle.truffle.espresso.impl.ParserMethod; import com.oracle.truffle.espresso.jdwp.api.ErrorCodes; import com.oracle.truffle.espresso.jdwp.api.Ids; import com.oracle.truffle.espresso.jdwp.api.KlassRef; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefineListener; import com.oracle.truffle.espresso.runtime.Attribute; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; @@ -65,6 +75,14 @@ public final class ClassRedefinition { private static volatile boolean locked = false; private static Thread redefineThread = null; + private final EspressoContext context; + private final Ids ids; + private final RedefineListener redefineListener; + + public void addExtraReloadClasses(List redefineInfos, List additional) { + redefineListener.addExtraReloadClasses(redefineInfos, additional); + } + private enum RedefinitionSupport { METHOD_BODY, ADD_METHOD, @@ -86,6 +104,12 @@ enum ClassChange { INVALID; } + public ClassRedefinition(EspressoContext context, Ids ids, RedefineListener listener) { + this.context = context; + this.ids = ids; + this.redefineListener = listener; + } + public static void lock() { synchronized (redefineLock) { check(); @@ -126,6 +150,10 @@ public static void end() { } } + public void runPostRedefintionListeners(ObjectKlass[] changedKlasses) { + redefineListener.postRedefition(changedKlasses); + } + private static class RedefineAssumption { private final Assumption assumption = Truffle.getRuntime().createAssumption(); } @@ -153,7 +181,7 @@ public static void check() { } } - public static List detectClassChanges(HotSwapClassInfo[] classInfos, EspressoContext context) { + public List detectClassChanges(HotSwapClassInfo[] classInfos) throws RedefintionNotSupportedException { List result = new ArrayList<>(classInfos.length); for (HotSwapClassInfo hotSwapInfo : classInfos) { KlassRef klass = hotSwapInfo.getKlass(); @@ -185,40 +213,46 @@ public static List detectClassChanges(HotSwapClassInfo[] classInfo return result; } - public static int redefineClass(ChangePacket packet, Ids ids, EspressoContext context, List refreshSubClasses) { + public int redefineClass(ChangePacket packet, List refreshSubClasses) { try { switch (packet.classChange) { case METHOD_BODY_CHANGE: case CONSTANT_POOL_CHANGE: case CLASS_NAME_CHANGED: - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; case ADD_METHOD: if (isAddMethodSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.ADD_METHOD_NOT_IMPLEMENTED; } case REMOVE_METHOD: if (isRemoveMethodSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.DELETE_METHOD_NOT_IMPLEMENTED; } case SCHEMA_CHANGE: if (isArbitraryChangesSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.SCHEMA_CHANGE_NOT_IMPLEMENTED; } case CLASS_MODIFIERS_CHANGE: if (isArbitraryChangesSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED; } case HIERARCHY_CHANGE: if (isArbitraryChangesSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.HIERARCHY_CHANGE_NOT_IMPLEMENTED; } @@ -263,7 +297,7 @@ private static boolean isRemoveMethodSupported() { // detect all types of class changes, but return early when a change that require arbitrary // changes - private static ClassChange detectClassChanges(ParserKlass newParserKlass, ObjectKlass oldKlass, DetectedChange collectedChanges, ParserKlass finalParserKlass) { + private static ClassChange detectClassChanges(ParserKlass newParserKlass, ObjectKlass oldKlass, DetectedChange collectedChanges, ParserKlass finalParserKlass) throws RedefintionNotSupportedException { ClassChange result = ClassChange.NO_CHANGE; ParserKlass oldParserKlass = oldKlass.getLinkedKlass().getParserKlass(); boolean isPatched = finalParserKlass != null; @@ -605,7 +639,7 @@ private static boolean isUnchangedField(Field oldField, ParserField newField) { return false; } - private static int doRedefineClass(ChangePacket packet, Ids ids, EspressoContext context, List refreshSubClasses) { + private void doRedefineClass(ChangePacket packet, List refreshSubClasses) { ObjectKlass oldKlass = packet.info.getKlass(); ClassRegistry classRegistry = context.getRegistries().getClassRegistry(packet.info.getClassLoader()); if (packet.info.isRenamed()) { @@ -629,7 +663,9 @@ private static int doRedefineClass(ChangePacket packet, Ids ids, Espress context.getRegistries().recordConstraint(type, oldKlass, oldKlass.getDefiningClassLoader()); } oldKlass.redefineClass(packet, refreshSubClasses, ids); - return 0; + if (redefineListener.rerunClinit(oldKlass, packet.detectedChange.clinitChanged())) { + oldKlass.reRunClinit(); + } } @TruffleBoundary diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DefineKlassListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DefineKlassListener.java similarity index 91% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DefineKlassListener.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DefineKlassListener.java index 1f91325ba4f5..68c97609a2ff 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DefineKlassListener.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DefineKlassListener.java @@ -20,7 +20,9 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; + +import com.oracle.truffle.espresso.impl.ObjectKlass; public interface DefineKlassListener { void onKlassDefined(ObjectKlass klass); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DetectedChange.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DetectedChange.java similarity index 76% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DetectedChange.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DetectedChange.java index 905333627a28..f52f0176f8d4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DetectedChange.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DetectedChange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, 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 @@ -20,7 +20,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; + +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ParserMethod; import java.util.ArrayList; import java.util.Collections; @@ -30,25 +35,29 @@ import java.util.Map; import java.util.Set; -final class DetectedChange { +public final class DetectedChange { private final Map changedMethodBodies = new HashMap<>(); private final List addedMethods = new ArrayList<>(); private final Set removedMethods = new HashSet<>(); private final List outerFields = new ArrayList<>(); + private boolean clinitChanged; void addMethodBodyChange(Method oldMethod, ParserMethod newMethod) { changedMethodBodies.put(oldMethod, newMethod); + if (oldMethod.getName() == Symbol.Name._clinit_) { + clinitChanged = true; + } } - Map getChangedMethodBodies() { + public Map getChangedMethodBodies() { return Collections.unmodifiableMap(changedMethodBodies); } - List getAddedMethods() { + public List getAddedMethods() { return Collections.unmodifiableList(addedMethods); } - Set getRemovedMethods() { + public Set getRemovedMethods() { return Collections.unmodifiableSet(removedMethods); } @@ -75,4 +84,8 @@ public void addOuterField(Field oldField) { public List getOuterFields() { return Collections.unmodifiableList(outerFields); } + + public boolean clinitChanged() { + return clinitChanged; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/HotSwapClassInfo.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/HotSwapClassInfo.java similarity index 98% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/HotSwapClassInfo.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/HotSwapClassInfo.java index 82378d009a61..a3330169b12d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/HotSwapClassInfo.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/HotSwapClassInfo.java @@ -20,9 +20,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.runtime.StaticObject; import java.lang.ref.WeakReference; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ImmutableClassInfo.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ImmutableClassInfo.java similarity index 97% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ImmutableClassInfo.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ImmutableClassInfo.java index f393442b6c44..4d83f05aa95a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ImmutableClassInfo.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ImmutableClassInfo.java @@ -20,9 +20,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.runtime.StaticObject; import java.lang.ref.WeakReference; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/InnerClassRedefiner.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java similarity index 91% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/InnerClassRedefiner.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java index c4b4b6a48126..db2bb2e0f7ca 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/InnerClassRedefiner.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java @@ -20,10 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import com.oracle.truffle.espresso.classfile.ClassfileParser; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ClassRegistry; +import com.oracle.truffle.espresso.impl.ConstantPoolPatcher; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.jdwp.api.ErrorCodes; import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; @@ -35,7 +39,6 @@ import java.lang.instrument.IllegalClassFormatException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -46,7 +49,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public final class InnerClassRedefiner implements DefineKlassListener { +public final class InnerClassRedefiner { public static final Pattern ANON_INNER_CLASS_PATTERN = Pattern.compile(".*\\$\\d+.*"); public static final int METHOD_FINGERPRINT_EQUALS = 8; @@ -72,11 +75,11 @@ public InnerClassRedefiner(EspressoContext context) { this.context = context; } - public HotSwapClassInfo[] matchAnonymousInnerClasses(RedefineInfo[] redefineInfos, List removedInnerClasses) { + public HotSwapClassInfo[] matchAnonymousInnerClasses(List redefineInfos, List removedInnerClasses) throws RedefintionNotSupportedException { hotswapState.clear(); - ArrayList unhandled = new ArrayList<>(redefineInfos.length); - Collections.addAll(unhandled, redefineInfos); - Map, HotSwapClassInfo> handled = new HashMap<>(redefineInfos.length); + ArrayList unhandled = new ArrayList<>(redefineInfos); + + Map, HotSwapClassInfo> handled = new HashMap<>(redefineInfos.size()); // build inner/outer relationship from top-level to leaf class in order // each round below handles classes where the outer class was previously // handled @@ -149,7 +152,7 @@ private static void collectAllHotswapClasses(Collection infos, } } - private void fetchMissingInnerClasses(HotSwapClassInfo hotswapInfo) { + private void fetchMissingInnerClasses(HotSwapClassInfo hotswapInfo) throws RedefintionNotSupportedException { StaticObject definingLoader = hotswapInfo.getClassLoader(); ArrayList> innerNames = new ArrayList<>(1); @@ -215,7 +218,7 @@ private Symbol getOuterClassName(Symbol innerName) { return context.getNames().getOrCreate(strName.substring(0, strName.lastIndexOf('$'))); } - private void matchClassInfo(HotSwapClassInfo hotSwapInfo, List removedInnerClasses, Map, Symbol>> renamingRules) { + private void matchClassInfo(HotSwapClassInfo hotSwapInfo, List removedInnerClasses, Map, Symbol>> renamingRules) throws RedefintionNotSupportedException { Klass klass = hotSwapInfo.getKlass(); // try to fetch all direct inner classes // based on the constant pool in the class bytes @@ -326,7 +329,12 @@ Set findLoadedInnerClasses(Klass klass) { // register a listener on the registry to fill in // future loaded anonymous inner classes ClassRegistry classRegistry = context.getRegistries().getClassRegistry(klass.getDefiningClassLoader()); - classRegistry.registerOnLoadListener(this); + classRegistry.registerOnLoadListener(new DefineKlassListener() { + @Override + public void onKlassDefined(ObjectKlass objectKlass) { + InnerClassRedefiner.this.onKlassDefined(objectKlass); + } + }); // do a one-time look up of all currently loaded // classes for this loader and fill in the map @@ -334,15 +342,18 @@ Set findLoadedInnerClasses(Klass klass) { for (Klass loadedKlass : loadedKlasses) { if (loadedKlass instanceof ObjectKlass) { ObjectKlass objectKlass = (ObjectKlass) loadedKlass; - Symbol klassName = loadedKlass.getName(); - if (klassName.toString().contains("$")) { - Symbol outerType = context.getTypes().fromName(getOuterClassName(klassName)); - Set innerKlasses = classLoaderMap.get(outerType); - if (innerKlasses == null) { - innerKlasses = new HashSet<>(1); - classLoaderMap.put(outerType, innerKlasses); + Matcher matcher = ANON_INNER_CLASS_PATTERN.matcher(klass.getNameAsString()); + if (matcher.matches()) { + Symbol outerClassName = getOuterClassName(loadedKlass.getName()); + if (outerClassName != null && outerClassName.length() > 0) { + Symbol outerType = context.getTypes().fromName(outerClassName); + Set innerKlasses = classLoaderMap.get(outerType); + if (innerKlasses == null) { + innerKlasses = new HashSet<>(1); + classLoaderMap.put(outerType, innerKlasses); + } + innerKlasses.add(objectKlass); } - innerKlasses.add(objectKlass); } } } @@ -353,8 +364,7 @@ Set findLoadedInnerClasses(Klass klass) { return innerClasses != null ? innerClasses : new HashSet<>(0); } - @Override - public void onKlassDefined(ObjectKlass klass) { + private void onKlassDefined(ObjectKlass klass) { Matcher matcher = ANON_INNER_CLASS_PATTERN.matcher(klass.getNameAsString()); if (matcher.matches()) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/RedefintionNotSupportedException.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/RedefintionNotSupportedException.java similarity index 83% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/RedefintionNotSupportedException.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/RedefintionNotSupportedException.java index b5f8e409baf5..c2d3cbecde85 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/RedefintionNotSupportedException.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/RedefintionNotSupportedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2021, 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 @@ -20,14 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; -public class RedefintionNotSupportedException extends RuntimeException { +public class RedefintionNotSupportedException extends Exception { private static final long serialVersionUID = -5767957395371919542L; private final int errorCode; - RedefintionNotSupportedException(int errorCode) { + public RedefintionNotSupportedException(int errorCode) { this.errorCode = errorCode; } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodBreakpoint.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/ClassLoadAction.java similarity index 80% rename from espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodBreakpoint.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/ClassLoadAction.java index 12ce73cc3667..e23a4b73acdf 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodBreakpoint.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/ClassLoadAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2021, 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 @@ -20,8 +20,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.jdwp.api; +package com.oracle.truffle.espresso.redefinition.plugins.api; -public interface MethodBreakpoint { - int getRequestId(); +import com.oracle.truffle.espresso.jdwp.api.KlassRef; + +public interface ClassLoadAction { + void fire(KlassRef klass); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java new file mode 100644 index 000000000000..02dc68091ffd --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import java.util.Collection; +import java.util.List; + +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.RedefineInfo; +import com.oracle.truffle.espresso.redefinition.plugins.impl.CachedRedefineObject; +import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefinitionPluginHandler; +import com.oracle.truffle.espresso.redefinition.plugins.impl.UncachedRedefineObject; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; +import com.oracle.truffle.espresso.substitutions.Host; + +public abstract class InternalRedefinitionPlugin { + + private static final String constructorName = ""; + + private EspressoContext context; + private RedefinitionPluginHandler redefinitionPluginHandler; + + public EspressoContext getContext() { + return context; + } + + public void activate(EspressoContext espressoContext, RedefinitionPluginHandler handler) { + this.context = espressoContext; + this.redefinitionPluginHandler = handler; + } + + public abstract String getName(); + + public abstract TriggerClass[] getTriggerClasses(); + + public boolean reRunClinit(@SuppressWarnings("unused") KlassRef klass, @SuppressWarnings("unused") boolean changed) { + return false; + } + + public void fillExtraReloadClasses(@SuppressWarnings("unused") List redefineInfos, @SuppressWarnings("unused") List additional) { + // default does nothing + } + + public void postClassRedefinition(@SuppressWarnings("unused") KlassRef[] changedKlasses) { + // default does nothing + } + + public static RedefineObject createUncached(Object instance) { + return new UncachedRedefineObject((StaticObject) instance); + } + + public static RedefineObject createCached(Object instance) { + return new CachedRedefineObject((StaticObject) instance); + } + + public static RedefineObject createCached(KlassRef klass) { + return new CachedRedefineObject(klass); + } + + protected KlassRef getreflectedKlassType(Object classObject) { + return context.getJdwpContext().getReflectedType(classObject); + } + + protected void registerClassLoadAction(String className, ClassLoadAction action) { + redefinitionPluginHandler.registerClassLoadAction(className, action); + } + + protected void hookMethodExit(KlassRef klass, MethodLocator hookSpec, MethodHook.Kind kind, MethodExitHook onExitHook) { + for (MethodRef method : klass.getDeclaredMethodRefs()) { + if (method.getNameAsString().equals(hookSpec.getName()) && method.getSignatureAsString().equals(hookSpec.getSignature())) { + method.addMethodHook(new RedefintionHook(onExitHook, kind)); + break; + } + } + } + + protected void hookMethodEntry(KlassRef klass, MethodLocator hookSpec, MethodHook.Kind kind, MethodEntryHook onEntryHook) { + for (MethodRef method : klass.getDeclaredMethodRefs()) { + if (method.getNameAsString().equals(hookSpec.getName()) && method.getSignatureAsString().equals(hookSpec.getSignature())) { + method.addMethodHook(new RedefintionHook(onEntryHook, kind)); + break; + } + } + } + + protected void hookConstructor(KlassRef klass, MethodHook.Kind kind, MethodEntryHook onEntryHook) { + for (MethodRef method : klass.getDeclaredMethodRefs()) { + if (method.getNameAsString().equals(constructorName)) { + method.addMethodHook(new RedefintionHook(onEntryHook, kind)); + } + } + } + + protected void clearCollection(@Host(Collection.class) RedefineObject object, String fieldName) throws NoSuchFieldException, NoSuchMethodException { + RedefineObject collectionField = object.getInstanceField(fieldName); + if (collectionField != null) { + collectionField.invokeRaw("clear"); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java new file mode 100644 index 000000000000..64051524f18c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVariable; + +public interface MethodEntryHook { + void onMethodEnter(MethodRef method, MethodVariable[] variables); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java new file mode 100644 index 000000000000..f9fa5eb5eec5 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.MethodRef; + +public interface MethodExitHook { + void onMethodExit(MethodRef method, Object returnValue); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java new file mode 100644 index 000000000000..7b2137a35dcd --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +public final class MethodLocator { + private final String name; + private final String signature; + + public MethodLocator(String name, String signature) { + this.name = name; + this.signature = signature; + } + + public String getName() { + return name; + } + + public String getSignature() { + return signature; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java new file mode 100644 index 000000000000..60657c5cc659 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.KlassRef; + +public interface RedefineObject { + boolean notNull(); + + KlassRef getKlass(); + + RedefineObject fromType(String className); + + RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException; + + RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException; + + RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java new file mode 100644 index 000000000000..f084ffc291c1 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java @@ -0,0 +1,7 @@ +package com.oracle.truffle.espresso.redefinition.plugins.api; + +import java.util.Set; + +public interface RedefinitionAction { + Set onChange(); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java new file mode 100644 index 000000000000..87a5c622113b --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.MethodHook; +import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVariable; + +public final class RedefintionHook implements MethodHook { + + private final MethodExitHook onExitHook; + private final MethodEntryHook onEntryHook; + private final Kind kind; + + public RedefintionHook(MethodEntryHook onEntryHook, Kind kind) { + this.onExitHook = null; + this.onEntryHook = onEntryHook; + this.kind = kind; + } + + public RedefintionHook(MethodExitHook onExitHook, Kind kind) { + this.onExitHook = onExitHook; + this.onEntryHook = null; + this.kind = kind; + } + + @Override + public Kind getKind() { + return kind; + } + + @Override + public boolean onMethodEnter(MethodRef method, MethodVariable[] variables) { + if (onEntryHook != null) { + onEntryHook.onMethodEnter(method, variables); + } + return false; + } + + @Override + public boolean onMethodExit(MethodRef method, Object returnValue) { + if (onExitHook != null) { + onExitHook.onMethodExit(method, returnValue); + } + return false; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java new file mode 100644 index 000000000000..748ee0b84a0c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.KlassRef; + +public final class TriggerClass { + + private String className; + private final InternalRedefinitionPlugin plugin; + private final TriggerClassHook hook; + + public TriggerClass(String className, InternalRedefinitionPlugin plugin, TriggerClassHook hook) { + this.className = className; + this.plugin = plugin; + this.hook = hook; + } + + public String getClassName() { + return className; + } + + public InternalRedefinitionPlugin getPlugin() { + return plugin; + } + + public void fire(KlassRef klass) { + hook.fire(klass); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java new file mode 100644 index 000000000000..a945fb986193 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.KlassRef; + +public interface TriggerClassHook { + void fire(KlassRef klass); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java new file mode 100644 index 000000000000..de4b5069f218 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import java.util.HashMap; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.jdwp.api.KlassRef; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; + +public final class CachedRedefineObject extends RedefineObjectImpl { + + protected final EspressoContext context; + private final HashMap methodsCache = new HashMap<>(1); + private final HashMap fieldsCache = new HashMap<>(1); + + public CachedRedefineObject(StaticObject object) { + super(object); + this.context = object.getKlass().getContext(); + } + + public CachedRedefineObject(KlassRef klass) { + super(klass); + this.context = ((Klass) klass).getContext(); + } + + @Override + @TruffleBoundary + public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException { + StringBuilder stringBuffer = new StringBuilder(name); + for (RedefineObject arg : args) { + stringBuffer.append(((RedefineObjectImpl) arg).instance.get().getKlass().getNameAsString()); + } + String mapKey = stringBuffer.toString(); + Method method = methodsCache.get(mapKey); + if (method == null) { + method = lookupMethod(name, args); + if (method != null) { + methodsCache.put(mapKey, method); + } + } + if (method != null) { + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException("cannot invoke method on garbage collection instance"); + } + RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; + for (int i = 0; i < args.length; i++) { + internalArgs[i] = (RedefineObjectImpl) args[i]; + } + return InternalRedefinitionPlugin.createUncached(method.invokeDirect(theInstance, rawObjects(internalArgs))); + } + throw new NoSuchMethodException(); + } + + @Override + public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException, IllegalStateException { + // fetch the known declaring class of the method + StaticObject theInstance = instance.get(); + if (instance == null) { + throw new IllegalStateException(); + } + Symbol type = context.getTypes().fromClassGetName(className); + Klass klassRef = context.getRegistries().findLoadedClass(type, klass.getDefiningClassLoader()); + if (klassRef != null) { + Method method = lookupMethod(klassRef, methodName, args); + if (method != null) { + RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; + for (int i = 0; i < args.length; i++) { + internalArgs[i] = (RedefineObjectImpl) args[i]; + } + return InternalRedefinitionPlugin.createUncached(method.invokeDirect(theInstance, rawObjects(internalArgs))); + } + } + throw new NoSuchMethodException(); + } + + @Override + public RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException { + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException("cannot get field on garbage collection instance"); + } + Field field = fieldsCache.get(fieldName); + if (field == null) { + Klass klassRef = klass; + while (klassRef != null) { + for (Field declaredField : klassRef.getDeclaredFields()) { + if (declaredField.getNameAsString().equals(fieldName)) { + field = declaredField; + fieldsCache.put(fieldName, field); + break; + } + } + klassRef = klassRef.getSuperKlass(); + } + if (field == null) { + throw new NoSuchFieldException(); + } + } + return InternalRedefinitionPlugin.createUncached(field.get(theInstance)); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java new file mode 100644 index 000000000000..16158bec18fa --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import com.oracle.truffle.api.interop.ArityException; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.runtime.StaticObject; + +final class ExternalPluginHandler { + + private static InteropLibrary INTEROP; + private static final String RERUN_CLINIT = "rerunClassInit"; + private static final String POST_HOTSWAP = "postHotSwap"; + + private final StaticObject guestHandler; + + private ExternalPluginHandler(StaticObject handler) { + this.guestHandler = handler; + } + + public static ExternalPluginHandler create(StaticObject guestHandler) throws IllegalArgumentException { + INTEROP = InteropLibrary.getFactory().create(guestHandler); + + boolean invocable = INTEROP.isMemberInvocable(guestHandler, RERUN_CLINIT) && + INTEROP.isMemberInvocable(guestHandler, POST_HOTSWAP); + + if (!invocable) { + throw new IllegalArgumentException("guest handler does not implement expected API"); + } + return new ExternalPluginHandler(guestHandler); + } + + public boolean rerunClassInit(Klass klass, boolean changed) { + try { + return (boolean) INTEROP.invokeMember(guestHandler, RERUN_CLINIT, klass.mirror(), changed); + } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException | ArityException e) { + // TODO - log failure to invoke clinit rerun method on external plugin handler + } + return false; + } + + public void postHotSwap(Klass[] changedKlasses) { + try { + StaticObject[] guestClasses = new StaticObject[changedKlasses.length]; + for (int i = 0; i < guestClasses.length; i++) { + guestClasses[i] = changedKlasses[i].mirror(); + } + StaticObject array = StaticObject.createArray(changedKlasses[0].getMeta().java_lang_Class_array, guestClasses); + INTEROP.invokeMember(guestHandler, POST_HOTSWAP, array); + } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException | ArityException e) { + // TODO - log failure to invoke clinit rerun method on external plugin handler + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java new file mode 100644 index 000000000000..74f533381718 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import java.util.List; + +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; + +public interface RedefineListener { + boolean rerunClinit(ObjectKlass klass, boolean changed); + + void postRedefition(ObjectKlass[] changedKlasses); + + void addExtraReloadClasses(List redefineInfos, List additional); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java new file mode 100644 index 000000000000..66de8d0f2491 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.jdwp.api.KlassRef; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; + +import java.lang.ref.WeakReference; + +public abstract class RedefineObjectImpl implements RedefineObject { + + protected final WeakReference instance; + protected final Klass klass; + + protected RedefineObjectImpl(StaticObject object) { + this.instance = new WeakReference<>(object); + this.klass = object.getKlass(); + } + + public RedefineObjectImpl(KlassRef klass) { + this.instance = new WeakReference<>(StaticObject.NULL); + this.klass = (Klass) klass; + } + + @Override + public Klass getKlass() { + return klass; + } + + @Override + public abstract RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException; + + @SuppressWarnings("unused") + public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException { + throw new NoSuchMethodException(className + "." + methodName); + } + + @Override + public abstract RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; + + protected Method lookupMethod(String name, RedefineObject[] args) throws NoSuchMethodException { + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException("cannot invoke method on garbage collection instance"); + } + + Klass currentKlass = klass; + + while (currentKlass != null) { + Method method = lookupMethod(currentKlass, name, args); + if (method != null) { + return method; + } + ObjectKlass[] interfaces = currentKlass.getInterfaces(); + for (ObjectKlass itf : interfaces) { + method = lookupMethod(itf, name, args); + if (method != null && !method.isAbstract()) { + return method; + } + } + currentKlass = currentKlass.getSuperKlass(); + } + return null; + } + + protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args) throws NoSuchMethodException { + for (Method declaredMethod : klassRef.getDeclaredMethods()) { + if (declaredMethod.getNameAsString().equals(name)) { + // match arguments + boolean match = true; + if (declaredMethod.getParameterCount() == args.length) { + Klass[] parameters = declaredMethod.resolveParameterKlasses(); + for (int i = 0; i < args.length; i++) { + if (!parameters[i].isAssignableFrom((Klass) args[i].getKlass())) { + match = false; + break; + } + } + } else if (declaredMethod.isVarargs()) { + // TODO - not implemented yet + throw new NoSuchMethodException("varargs lookup not implemented"); + } + if (match) { + return declaredMethod; + } + } + } + return null; + } + + protected Object[] rawObjects(RedefineObjectImpl[] args) { + Object[] result = new Object[args.length]; + for (int i = 0; i < args.length; i++) { + result[i] = args[i].instance; + } + return result; + } + + public boolean notNull() { + return instance != null && instance.get() != StaticObject.NULL; + } + + public RedefineObject fromType(String className) { + EspressoContext context = instance.get().getKlass().getContext(); + Symbol type = context.getTypes().fromClassGetName(className); + Klass loadedClass = context.getRegistries().findLoadedClass(type, instance.get().getKlass().getDefiningClassLoader()); + if (loadedClass != null) { + return new CachedRedefineObject(loadedClass.mirror()); + } + return null; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java new file mode 100644 index 000000000000..8f271442793e --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; +import com.oracle.truffle.espresso.redefinition.ClassLoadListener; +import com.oracle.truffle.espresso.redefinition.plugins.api.ClassLoadAction; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; + +public final class RedefinitionPluginHandler implements RedefineListener, ClassLoadListener { + + private final EspressoContext context; + private final Set internalPlugins = Collections.synchronizedSet(new HashSet<>(1)); + private final Map, Set> internalTriggers; + private final Map, List> classLoadActions = Collections.synchronizedMap(new HashMap<>()); + + // The guest language HotSwap plugin handler passed + // onto us if guest plugins are present at runtime. + private ExternalPluginHandler externalPluginHandler; + + private RedefinitionPluginHandler(EspressoContext espressoContext, Map, Set> triggers) { + this.context = espressoContext; + this.internalTriggers = triggers; + } + + @TruffleBoundary + public void registerClassLoadAction(String className, ClassLoadAction action) { + Symbol type = context.getTypes().fromClassGetName(className); + List list = classLoadActions.get(type); + if (list == null) { + list = Collections.synchronizedList(new ArrayList<>()); + classLoadActions.put(type, list); + } + list.add(action); + } + + public void registerExternalHotSwapHandler(StaticObject handler) { + if (handler != null) { + externalPluginHandler = ExternalPluginHandler.create(handler); + } + } + + public static RedefinitionPluginHandler create(EspressoContext espressoContext) { + // we use ServiceLoader to load all Espresso internal Plugins + ServiceLoader serviceLoader = ServiceLoader.load(InternalRedefinitionPlugin.class); + Iterator pluginIterator = serviceLoader.iterator(); + + Map, Set> triggers = new HashMap<>(); + while (pluginIterator.hasNext()) { + InternalRedefinitionPlugin plugin = pluginIterator.next(); + for (TriggerClass triggerClass : plugin.getTriggerClasses()) { + Symbol triggerType = espressoContext.getTypes().fromClassGetName(triggerClass.getClassName()); + Set triggerClasses = triggers.get(triggerType); + if (triggerClasses == null) { + triggerClasses = new HashSet<>(1); + } + triggerClasses.add(triggerClass); + triggers.put(triggerType, triggerClasses); + } + } + RedefinitionPluginHandler handler = new RedefinitionPluginHandler(espressoContext, triggers); + espressoContext.getRegistries().registerListener(handler); + return handler; + } + + @TruffleBoundary + @Override + public void onClassLoad(ObjectKlass klass) { + // internal plugins + Symbol type = klass.getType(); + if (internalTriggers.containsKey(type)) { + Set triggerClasses = internalTriggers.get(type); + for (TriggerClass triggerClass : triggerClasses) { + if (!internalPlugins.contains(triggerClass.getPlugin())) { + triggerClass.getPlugin().activate(klass.getContext(), this); + internalPlugins.add(triggerClass.getPlugin()); + } + triggerClass.fire(klass); + } + } + // fire registered load actions + List loadActions = classLoadActions.getOrDefault(type, Collections.emptyList()); + Iterator it = loadActions.iterator(); + while (it.hasNext()) { + ClassLoadAction loadAction = it.next(); + loadAction.fire(klass); + it.remove(); + } + if (loadActions.isEmpty()) { + classLoadActions.remove(type); + } + } + + // listener methods + @Override + public boolean rerunClinit(ObjectKlass klass, boolean changed) { + // internal plugins + for (InternalRedefinitionPlugin plugin : internalPlugins) { + if (plugin.reRunClinit(klass, changed)) { + return true; + } + } + // external plugins + if (externalPluginHandler != null) { + return externalPluginHandler.rerunClassInit(klass, changed); + } + return false; + } + + @Override + public void postRedefition(ObjectKlass[] changedKlasses) { + // internal plugins + for (InternalRedefinitionPlugin plugin : internalPlugins) { + plugin.postClassRedefinition(changedKlasses); + } + // external plugins + if (externalPluginHandler != null) { + externalPluginHandler.postHotSwap(changedKlasses); + } + } + + @Override + public void addExtraReloadClasses(List redefineInfos, List additional) { + // internal plugins + for (InternalRedefinitionPlugin plugin : internalPlugins) { + plugin.fillExtraReloadClasses(redefineInfos, additional); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java new file mode 100644 index 000000000000..251b354f5101 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.runtime.StaticObject; + +public final class UncachedRedefineObject extends RedefineObjectImpl { + public UncachedRedefineObject(StaticObject object) { + super(object); + } + + @Override + public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException { + Method method = lookupMethod(name, args); + if (method != null) { + RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; + for (int i = 0; i < args.length; i++) { + internalArgs[i] = (RedefineObjectImpl) args[i]; + } + return InternalRedefinitionPlugin.createUncached(method.invokeDirect(instance, rawObjects(internalArgs))); + } + throw new NoSuchMethodException(); + } + + @Override + public RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException { + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException("cannot get field on garbage collection instance"); + } + Klass klassRef = klass; + while (klassRef != null) { + for (Field declaredField : klassRef.getDeclaredFields()) { + if (declaredField.getNameAsString().equals(fieldName)) { + return InternalRedefinitionPlugin.createUncached(declaredField.get(theInstance)); + } + } + klassRef = klassRef.getSuperKlass(); + } + throw new NoSuchFieldException(); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java new file mode 100644 index 000000000000..adc37157573c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.jdkproxy; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +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.MethodVariable; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; +import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; + +public class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { + + public static final InteropLibrary INTEROP = InteropLibrary.getUncached(); + + private static final String PROXY_GENERATOR_CLASS = "sun.misc.ProxyGenerator"; + private static final String GENERATOR_METHOD = "generateProxyClass"; + private static final String GENERATOR_METHOD_SIG = "(Ljava/lang/String;[Ljava/lang/Class;I)[B"; + + private final Map> cache = Collections.synchronizedMap(new HashMap<>()); + + private MethodRef proxyGeneratorMethod; + + private ThreadLocal generationInProgress = ThreadLocal.withInitial(() -> false); + + @Override + public String getName() { + return "JDK Dynamic Proxy Reloading Plugin"; + } + + @Override + public TriggerClass[] getTriggerClasses() { + return new TriggerClass[]{new TriggerClass(PROXY_GENERATOR_CLASS, this, klass -> { + // hook into the proxy generator method to obtain proxy generation arguments + hookMethodEntry(klass, new MethodLocator(GENERATOR_METHOD, GENERATOR_METHOD_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { + if (generationInProgress.get()) { + // don't hook when we're re-generating proxy bytes + return; + } + if (proxyGeneratorMethod == null) { + proxyGeneratorMethod = method; + } + collectProxyArguments(variables); + }); + })}; + } + + private synchronized void collectProxyArguments(MethodVariable[] variables) { + Object[] proxyArgs = new Object[3]; + // proxy name + proxyArgs[0] = variables[0].getValue(); + // proxy interfaces + proxyArgs[1] = variables[1].getValue(); + // proxy access modifiers + proxyArgs[2] = variables[2].getValue(); + + try { + // fetch klass instances for the declared proxy interfaces + Object interfaces = proxyArgs[1]; + long arraySize = INTEROP.getArraySize(interfaces); + KlassRef[] proxyInterfaces = new KlassRef[(int) arraySize]; + for (int i = 0; i < arraySize; i++) { + // get the klass type of the interface + proxyInterfaces[i] = getreflectedKlassType(INTEROP.readArrayElement(interfaces, i)); + } + + // register onLoad action that will give us + // the klass object for the generated proxy + String proxyName = proxyArgs[0].toString(); + registerClassLoadAction(proxyName, klass -> { + ProxyCache proxyCache = new ProxyCache(klass, proxyArgs); + + // cache proxy arguments under each interface, so that + // when they change we can re-generate the proxy bytes + for (KlassRef proxyInterface : proxyInterfaces) { + addCacheEntry(proxyCache, proxyInterface); + } + }); + } catch (UnsupportedMessageException | InvalidArrayIndexException e) { + // TODO - log here. Should we have a dedicated HotSwap logger that logs to file? + } + } + + private void addCacheEntry(ProxyCache proxyCache, KlassRef proxyInterface) { + List list = cache.get(proxyInterface); + if (list == null) { + list = Collections.synchronizedList(new ArrayList<>()); + cache.put(proxyInterface, list); + } + list.add(proxyCache); + } + + @Override + @TruffleBoundary + public synchronized void fillExtraReloadClasses(List redefineInfos, List additional) { + for (RedefineInfo redefineInfo : redefineInfos) { + KlassRef klass = redefineInfo.getKlass(); + if (klass != null) { + List list = cache.getOrDefault(klass, Collections.emptyList()); + for (ProxyCache proxyCache : list) { + generationInProgress.set(true); + byte[] proxyBytes = (byte[]) proxyGeneratorMethod.invokeMethod(null, proxyCache.proxyArgs); + generationInProgress.set(false); + additional.add(new RedefineInfo(proxyCache.klass, proxyBytes)); + } + } + } + } + + @Override + public boolean reRunClinit(KlassRef klass, boolean changed) { + // changed Dynamic Proxy classes has cached Method references + // in static fields, so re-run the static initializer + return changed && klass.getNameAsString().contains("$Proxy"); + } + + private final class ProxyCache { + private final KlassRef klass; + private final Object[] proxyArgs; + + ProxyCache(KlassRef klass, Object[] proxyArgs) { + assert proxyArgs.length == 3; + this.klass = klass; + this.proxyArgs = proxyArgs; + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java new file mode 100644 index 000000000000..06d30a81c807 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.micronaut; + +import com.oracle.truffle.espresso.jdwp.api.KlassRef; +import com.oracle.truffle.espresso.jdwp.api.MethodHook; +import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; + +import java.util.ArrayList; + +// TODO - current state doesn't reflect changes to apps properly +// TODO - this needs more thorough integration to Micronaut +public class MicronautPlugin extends InternalRedefinitionPlugin { + + private static final String BEAN_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractBeanDefinitionReference"; + private static final String EXECUTION_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractExecutableMethod"; + private static final String DEFAULT_BEAN_CONTEXT_ACTIVATION_NAME = "io.micronaut.context.DefaultBeanContext"; + private static final String EMBEDDED_APPLICATION_CLASS_NAME = "io.micronaut.runtime.EmbeddedApplication"; + private static final String BEAN_LOCATOR_CLASS_NAME = "io.micronaut.context.BeanLocator"; + + private static final String START_METHOD_NAME = "start"; + private static final String START_METHOD_SIGNATURE = "()Lio/micronaut/context/BeanContext;"; + + private ArrayList clinitRerunTypes; + private boolean needsBeanRefresh; + + // the default bean context + private RedefineObject defaultBeanContext; + // the embedded application instance + private RedefineObject embeddedApplicationType; + + @Override + public String getName() { + return "Micronaut Reloading Plugin"; + } + + @Override + public TriggerClass[] getTriggerClasses() { + ArrayList triggers = new ArrayList<>(3); + triggers.add(new TriggerClass(DEFAULT_BEAN_CONTEXT_ACTIVATION_NAME, this, klass -> { + // default bean class loaded, so register a listener on the start method + // for fetching the context object we need on redefinitions later + hookMethodExit(klass, new MethodLocator(START_METHOD_NAME, START_METHOD_SIGNATURE), MethodHook.Kind.ONE_TIME, (method, returnValue) -> { + defaultBeanContext = InternalRedefinitionPlugin.createCached(returnValue); + }); + })); + triggers.add(new TriggerClass(BEAN_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); + triggers.add(new TriggerClass(EXECUTION_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); + return triggers.toArray(new TriggerClass[triggers.size()]); + } + + private synchronized void addClinitRerunKlass(KlassRef klass) { + if (clinitRerunTypes == null) { + clinitRerunTypes = new ArrayList<>(1); + } + clinitRerunTypes.add(klass); + } + + @Override + public boolean reRunClinit(KlassRef klass, boolean changed) { + if (changed) { + KlassRef superClass = klass.getSuperClass(); + for (KlassRef rerunType : clinitRerunTypes) { + if (rerunType == superClass) { + needsBeanRefresh = true; + return true; + } + } + } + return false; + } + + @Override + public void postClassRedefinition(KlassRef[] changedKlasses) { + if (needsBeanRefresh && defaultBeanContext.notNull()) { + try { + // clear bean caches + flushBeanCaches(); + // fetch needed class types and instances one time + if (embeddedApplicationType == null) { + embeddedApplicationType = defaultBeanContext.fromType(EMBEDDED_APPLICATION_CLASS_NAME); + } + defaultBeanContext.invokeRaw("startEnvironment"); + + // force a bean re-scan: + defaultBeanContext.invokeRaw("readAllBeanConfigurations"); + defaultBeanContext.invokeRaw("readAllBeanDefinitionClasses"); + + // re-wire beans for the application + defaultBeanContext.invokePrecise(BEAN_LOCATOR_CLASS_NAME, "findBean", embeddedApplicationType); + } catch (Throwable ex) { + JDWPLogger.log("Failed to reload Micronaut beans due to %s", JDWPLogger.LogLevel.ALL, ex.getMessage()); + } + } + needsBeanRefresh = false; + } + + private void flushBeanCaches() { + try { + clearCollection(defaultBeanContext, "singletonObjects"); + clearCollection(defaultBeanContext, "scopedProxies"); + clearCollection(defaultBeanContext, "beanDefinitionsClasses"); + clearCollection(defaultBeanContext, "containsBeanCache"); + clearCollection(defaultBeanContext, "initializedObjectsByType"); + clearCollection(defaultBeanContext, "beanConcreteCandidateCache"); + clearCollection(defaultBeanContext, "beanCandidateCache"); + clearCollection(defaultBeanContext, "beanIndex"); + } catch (NoSuchFieldException | NoSuchMethodException ex) { + JDWPLogger.log("Failed to flush Micronaut caches due to %s", JDWPLogger.LogLevel.ALL, ex.getMessage()); + } + } +} 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 e5ba9dae0593..3c8a8fe134c1 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 @@ -307,6 +307,10 @@ public String getMultiThreadingDisabledReason() { return multiThreadingDisabled; } + public JDWPContextImpl getJdwpContext() { + return jdwpContext; + } + /** * @return The {@link String}[] array passed to the main function. */ @@ -352,7 +356,10 @@ public void initializeContext() { spawnVM(); this.initialized = true; this.jdwpContext = new JDWPContextImpl(this); - this.eventListener = jdwpContext.jdwpInit(env, getMainThread()); + // enable JDWP instrumenter only if options are set (assumed valid if non-null) + if (JDWPOptions != null) { + this.eventListener = jdwpContext.jdwpInit(env, getMainThread()); + } referenceDrainer.startReferenceDrain(); } @@ -639,7 +646,9 @@ public T trackAllocation(T object) { } public void prepareDispose() { - jdwpContext.finalizeContext(); + if (jdwpContext != null) { + jdwpContext.finalizeContext(); + } } // region Agents 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 aaa4376a0fb6..0b21c22629bc 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 @@ -42,14 +42,9 @@ import com.oracle.truffle.espresso.bytecode.BytecodeStream; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.impl.ArrayKlass; -import com.oracle.truffle.espresso.impl.ChangePacket; -import com.oracle.truffle.espresso.impl.ClassRedefinition; -import com.oracle.truffle.espresso.impl.HotSwapClassInfo; -import com.oracle.truffle.espresso.impl.InnerClassRedefiner; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.impl.ObjectKlass; -import com.oracle.truffle.espresso.impl.RedefintionNotSupportedException; import com.oracle.truffle.espresso.jdwp.api.CallFrame; import com.oracle.truffle.espresso.jdwp.api.FieldRef; import com.oracle.truffle.espresso.jdwp.api.Ids; @@ -70,6 +65,12 @@ import com.oracle.truffle.espresso.nodes.EspressoInstrumentableNode; import com.oracle.truffle.espresso.nodes.EspressoRootNode; import com.oracle.truffle.espresso.nodes.quick.QuickNode; +import com.oracle.truffle.espresso.redefinition.ChangePacket; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.HotSwapClassInfo; +import com.oracle.truffle.espresso.redefinition.InnerClassRedefiner; +import com.oracle.truffle.espresso.redefinition.RedefintionNotSupportedException; +import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefinitionPluginHandler; import com.oracle.truffle.espresso.substitutions.Target_java_lang_Thread; public final class JDWPContextImpl implements JDWPContext { @@ -81,24 +82,24 @@ public final class JDWPContextImpl implements JDWPContext { private final Ids ids; private JDWPSetup setup; private VMListener eventListener = new EmptyListener(); + private final ClassRedefinition classRedefinition; private InnerClassRedefiner innerClassRedefiner; + private final RedefinitionPluginHandler redefinitionPluginHandler; public JDWPContextImpl(EspressoContext context) { this.context = context; this.ids = new Ids<>(StaticObject.NULL); this.setup = new JDWPSetup(); this.innerClassRedefiner = new InnerClassRedefiner(context); + this.redefinitionPluginHandler = RedefinitionPluginHandler.create(context); + this.classRedefinition = new ClassRedefinition(context, ids, redefinitionPluginHandler); } public VMListener jdwpInit(TruffleLanguage.Env env, Object mainThread) { - // enable JDWP instrumenter only if options are set (assumed valid if non-null) - if (context.JDWPOptions != null) { - Debugger debugger = env.lookup(env.getInstruments().get("debugger"), Debugger.class); - DebuggerController control = env.lookup(env.getInstruments().get(JDWPInstrument.ID), DebuggerController.class); - setup.setup(debugger, control, context.JDWPOptions, this, mainThread); - eventListener = control.getEventListener(); - } - return eventListener; + Debugger debugger = env.lookup(env.getInstruments().get("debugger"), Debugger.class); + DebuggerController control = env.lookup(env.getInstruments().get(JDWPInstrument.ID), DebuggerController.class); + setup.setup(debugger, control, context.JDWPOptions, this, mainThread); + return eventListener = control.getEventListener(); } public void finalizeContext() { @@ -685,55 +686,26 @@ public Node getInstrumentableNode(Node node) { } @Override - public synchronized int redefineClasses(RedefineInfo[] redefineInfos) { + public synchronized int redefineClasses(List redefineInfos) { try { - JDWPLogger.log("Redefining %d classes", JDWPLogger.LogLevel.REDEFINE, redefineInfos.length); - // list of sub classes that needs to refresh things like vtable - List refreshSubClasses = new ArrayList<>(); - - // match anon inner classes with previous state - List removedInnerClasses = new ArrayList<>(0); - HotSwapClassInfo[] matchedInfos = innerClassRedefiner.matchAnonymousInnerClasses(redefineInfos, removedInnerClasses); - - // detect all changes to all classes, throws if redefinition cannot be completed - // due to the nature of the changes - List changePackets = ClassRedefinition.detectClassChanges(matchedInfos, context); - - // We have to redefine super classes prior to subclasses - Collections.sort(changePackets, new HierarchyComparator()); - // begin redefine transaction ClassRedefinition.begin(); - for (ChangePacket packet : changePackets) { - JDWPLogger.log("Redefining class %s", JDWPLogger.LogLevel.REDEFINE, packet.info.getNewName()); - int result = ClassRedefinition.redefineClass(packet, getIds(), context, refreshSubClasses); - if (result != 0) { - return result; - } - } - // refresh subclasses when needed - Collections.sort(refreshSubClasses, new SubClassHierarchyComparator()); - for (ObjectKlass subKlass : refreshSubClasses) { - JDWPLogger.log("Updating sub class %s for redefined super class", JDWPLogger.LogLevel.REDEFINE, subKlass.getName()); - subKlass.onSuperKlassUpdate(); - } - // update the JWDP IDs - for (ChangePacket changePacket : changePackets) { - if (changePacket.info.isRenamed()) { - ObjectKlass klass = changePacket.info.getKlass(); - if (klass != null) { - ids.updateId(klass); - } - } - } + // list to collect all changed classes + List changedKlasses = new ArrayList<>(redefineInfos.size()); - // tell the InnerClassRedefiner to commit the changes to cache - innerClassRedefiner.commit(matchedInfos); + // redefine classes based on direct code changes first + doRedefine(redefineInfos, changedKlasses); - for (ObjectKlass removed : removedInnerClasses) { - removed.removeByRedefinition(); - } + // Now, collect additional classes to redefine in response + // to the redefined classes above + List additional = Collections.synchronizedList(new ArrayList<>()); + classRedefinition.addExtraReloadClasses(redefineInfos, additional); + // redefine additional classes now + doRedefine(additional, changedKlasses); + + // run post redefinition plugins + classRedefinition.runPostRedefintionListeners(changedKlasses.toArray(new ObjectKlass[changedKlasses.size()])); } catch (RedefintionNotSupportedException ex) { return ex.getErrorCode(); } finally { @@ -742,6 +714,63 @@ public synchronized int redefineClasses(RedefineInfo[] redefineInfos) { return 0; } + private void doRedefine(List redefineInfos, List changedKlasses) throws RedefintionNotSupportedException { + // list to hold removed inner classes that must be marked removed + List removedInnerClasses = new ArrayList<>(0); + // list of sub classes that needs to refresh things like vtable + List refreshSubClasses = new ArrayList<>(); + + // match anon inner classes with previous state + HotSwapClassInfo[] matchedInfos = innerClassRedefiner.matchAnonymousInnerClasses(redefineInfos, removedInnerClasses); + + // detect all changes to all classes, throws if redefinition cannot be completed + // due to the nature of the changes + List changePackets = classRedefinition.detectClassChanges(matchedInfos); + + // We have to redefine super classes prior to subclasses + Collections.sort(changePackets, new HierarchyComparator()); + + for (ChangePacket packet : changePackets) { + JDWPLogger.log("Redefining extra class %s", JDWPLogger.LogLevel.REDEFINE, packet.info.getNewName()); + int result = classRedefinition.redefineClass(packet, refreshSubClasses); + if (result != 0) { + throw new RedefintionNotSupportedException(result); + } + } + + // refresh subclasses when needed + Collections.sort(refreshSubClasses, new SubClassHierarchyComparator()); + for (ObjectKlass subKlass : refreshSubClasses) { + JDWPLogger.log("Updating sub class %s for redefined super class", JDWPLogger.LogLevel.REDEFINE, subKlass.getName()); + subKlass.onSuperKlassUpdate(); + } + + // include updated subclasses in all changed classes list + changedKlasses.addAll(refreshSubClasses); + + // update the JWDP IDs for renamed inner classes + for (ChangePacket changePacket : changePackets) { + ObjectKlass klass = changePacket.info.getKlass(); + changedKlasses.add(klass); + if (changePacket.info.isRenamed()) { + if (klass != null) { + ids.updateId(klass); + } + } + } + + // tell the InnerClassRedefiner to commit the changes to cache + innerClassRedefiner.commit(matchedInfos); + + for (ObjectKlass removed : removedInnerClasses) { + removed.removeByRedefinition(); + } + } + + public void registerExternalHotSwapHandler(StaticObject handler) { + redefinitionPluginHandler.registerExternalHotSwapHandler(handler); + } + private static class HierarchyComparator implements Comparator { public int compare(ChangePacket packet1, ChangePacket packet2) { Klass k1 = packet1.info.getKlass(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java new file mode 100644 index 000000000000..ed9d17b8b7a6 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021, 2021, 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.substitutions; + +import com.oracle.truffle.espresso.runtime.StaticObject; + +@EspressoSubstitutions +public final class Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler { + + @Substitution + public static boolean registerHandler(@Host(Object.class) StaticObject handler) { + assert handler != null; + try { + handler.getKlass().getContext().getJdwpContext().registerExternalHotSwapHandler(handler); + } catch (IllegalArgumentException ex) { + return false; + } + return true; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_reflect_NativeMethodAccessorImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_reflect_NativeMethodAccessorImpl.java index e0f894e6a5dd..b5e61a4cbc47 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_reflect_NativeMethodAccessorImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_reflect_NativeMethodAccessorImpl.java @@ -26,7 +26,7 @@ import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Name; import com.oracle.truffle.espresso.descriptors.Symbol.Type; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ObjectKlass; From 3ca6bda1a69417f63de30f717f91dddecb1b8e3c Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 15 Mar 2021 11:00:29 +0100 Subject: [PATCH 002/290] use proper isAssignableFrom to test if a class is really a Dynamic Proxy before rerunning clinit --- .../jdkproxy/JDKProxyRedefinitionPlugin.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index adc37157573c..0e4bbcb18d8a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -49,9 +49,12 @@ public class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { private static final String GENERATOR_METHOD = "generateProxyClass"; private static final String GENERATOR_METHOD_SIG = "(Ljava/lang/String;[Ljava/lang/Class;I)[B"; + private static final String PROXY_SUPER_CLASS = "java.lang.reflect.Proxy"; + private final Map> cache = Collections.synchronizedMap(new HashMap<>()); private MethodRef proxyGeneratorMethod; + private KlassRef proxySuperKlass; private ThreadLocal generationInProgress = ThreadLocal.withInitial(() -> false); @@ -62,7 +65,9 @@ public String getName() { @Override public TriggerClass[] getTriggerClasses() { - return new TriggerClass[]{new TriggerClass(PROXY_GENERATOR_CLASS, this, klass -> { + TriggerClass[] triggerClasses = new TriggerClass[2]; + // trigger on proxy generator class and add generator method hooks + triggerClasses[0] = new TriggerClass(PROXY_GENERATOR_CLASS, this, klass -> { // hook into the proxy generator method to obtain proxy generation arguments hookMethodEntry(klass, new MethodLocator(GENERATOR_METHOD, GENERATOR_METHOD_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { if (generationInProgress.get()) { @@ -74,7 +79,10 @@ public TriggerClass[] getTriggerClasses() { } collectProxyArguments(variables); }); - })}; + }); + // trigger on Proxy super class to obtain the type + triggerClasses[1] = new TriggerClass(PROXY_SUPER_CLASS, this, klass -> proxySuperKlass = klass); + return triggerClasses; } private synchronized void collectProxyArguments(MethodVariable[] variables) { @@ -109,7 +117,7 @@ private synchronized void collectProxyArguments(MethodVariable[] variables) { } }); } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - // TODO - log here. Should we have a dedicated HotSwap logger that logs to file? + // TODO - log here. Should we have a dedicated HotSwap logger that logs to file? } } @@ -143,7 +151,7 @@ public synchronized void fillExtraReloadClasses(List redefineInfos public boolean reRunClinit(KlassRef klass, boolean changed) { // changed Dynamic Proxy classes has cached Method references // in static fields, so re-run the static initializer - return changed && klass.getNameAsString().contains("$Proxy"); + return changed && proxySuperKlass.isAssignable(klass); } private final class ProxyCache { From b9937640ea2287dd17ad9a2822536b3c61faa814 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 17 Mar 2021 13:11:00 +0100 Subject: [PATCH 003/290] various fixes to redefinition internal API --- .../truffle/espresso/jdwp/api/MethodHook.java | 4 + .../truffle/espresso/jdwp/api/MethodRef.java | 3 + .../espresso/jdwp/impl/SocketConnection.java | 2 +- .../jdwp/impl/VMEventListenerImpl.java | 8 +- .../oracle/truffle/espresso/impl/Klass.java | 2 +- .../oracle/truffle/espresso/impl/Method.java | 28 ++++++ .../truffle/espresso/impl/ObjectKlass.java | 10 +- .../api/InternalRedefinitionPlugin.java | 10 +- .../plugins/api/RedefineObject.java | 5 +- .../plugins/api/RedefintionHook.java | 8 ++ .../plugins/impl/CachedRedefineObject.java | 4 +- .../plugins/impl/RedefineObjectImpl.java | 72 +++++++++++--- .../plugins/impl/UncachedRedefineObject.java | 4 +- .../jdkproxy/JDKProxyRedefinitionPlugin.java | 3 - .../plugins/micronaut/MicronautPlugin.java | 98 +++++++++---------- 15 files changed, 178 insertions(+), 83 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java index d7420f5ed6d9..6cc2a48c0a5e 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java @@ -38,4 +38,8 @@ default int getRequestId() { boolean onMethodEnter(MethodRef method, MethodVariable[] variables); boolean onMethodExit(MethodRef method, Object returnValue); + + default boolean hasFired() { + return false; + } } 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 f2cdcce9172a..d83c3b4e7671 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 @@ -195,6 +195,9 @@ public interface MethodRef { */ void removedMethodHook(int requestId); + + void removedMethodHook(MethodHook hook); + /** * Determines if there are any breakpoints set on this method. * diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/SocketConnection.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/SocketConnection.java index 6b354fc33323..6634a94b52af 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/SocketConnection.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/SocketConnection.java @@ -40,7 +40,7 @@ public final class SocketConnection implements Runnable { private final Object sendLock = new Object(); private final Object closeLock = new Object(); - private final BlockingQueue queue = new ArrayBlockingQueue<>(512); + private final BlockingQueue queue = new ArrayBlockingQueue<>(4096); SocketConnection(Socket socket, ServerSocket serverSocket) throws IOException { this.socket = socket; diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java index c8a964b38d39..e51ae017c1f1 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java @@ -182,7 +182,9 @@ public boolean onMethodEntry(MethodRef method, Object scope) { } switch (hook.getKind()) { case ONE_TIME: - method.removedMethodHook(-1); + if (hook.hasFired()) { + method.removedMethodHook(hook); + } break; case INDEFINITE: // leave the hook active @@ -205,7 +207,9 @@ public boolean onMethodReturn(MethodRef method, Object returnValue) { } switch (hook.getKind()) { case ONE_TIME: - method.removedMethodHook(-1); + if (hook.hasFired()) { + method.removedMethodHook(hook); + } break; case INDEFINITE: // leave the hook active diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index 06fc1d8f2fb4..18909f71ad3a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -1013,7 +1013,7 @@ int getHierarchyDepth() { @CompilationFinal(dimensions = 1) private Klass[] transitiveInterfaceCache; - protected final Klass[] getTransitiveInterfacesList() { + public final Klass[] getTransitiveInterfacesList() { Klass[] transitiveInterfaces = transitiveInterfaceCache; if (transitiveInterfaces == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); 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 1bdafd6bb0e5..116f1154cbeb 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 @@ -1043,6 +1043,29 @@ public void removeActiveHook(int requestId) { } } + public void removeActiveHook(MethodHook hook) { + // shrink the array to avoid null values + if (hooks.length == 0) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); + } else if (hooks.length == 1) { + hooks = new MethodHook[0]; + hasActiveHook.set(false); + } else { + int removeIndex = -1; + for (int i = 0; i < hooks.length; i++) { + if (hooks[i] == hook) { + removeIndex = i; + break; + } + } + 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; + } + } + public SharedRedefinitionContent redefine(ParserMethod newMethod, ParserKlass newKlass, Ids ids) { // invalidate old version // install the new method version immediately @@ -1387,6 +1410,11 @@ public void removedMethodHook(int requestId) { getMethod().removeActiveHook(requestId); } + @Override + public void removedMethodHook(MethodHook hook) { + getMethod().removeActiveHook(hook); + } + @Override public boolean hasActiveHook() { return getMethod().hasActiveHook(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index e6d1f6944f0a..6a30ca75f30b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1195,11 +1195,7 @@ public void redefineClass(ChangePacket packet, List refreshSubClass klassVersion = new KlassVersion(pool, linkedKlass, newDeclaredMethods, mirandaMethods, vtable, itable, iKlassTable); - // flush caches before invalidating to avoid races - // a potential thread fetching new reflection data - // will be blocked at entry until the redefinition - // transaction is ended - flushReflectionCaches(); + incrementKlassRedefinitionCount(); oldVersion.assumption.invalidate(); } @@ -1222,7 +1218,7 @@ private static void checkCopyMethods(Method method, Method[] table, Method.Share } } - private void flushReflectionCaches() { + private void incrementKlassRedefinitionCount() { // increment the redefine count on the class instance to flush reflection caches int value = InterpreterToVM.getFieldInt(mirror(), getMeta().java_lang_Class_classRedefinedCount); InterpreterToVM.setFieldInt(++value, mirror(), getMeta().java_lang_Class_classRedefinedCount); @@ -1273,7 +1269,7 @@ public void onSuperKlassUpdate() { // a potential thread fetching new reflection data // will be blocked at entry until the redefinition // transaction is ended - flushReflectionCaches(); + incrementKlassRedefinitionCount(); oldVersion.assumption.invalidate(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java index 02dc68091ffd..7402f0ce2b6d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java @@ -25,6 +25,8 @@ import java.util.Collection; import java.util.List; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; @@ -38,6 +40,8 @@ public abstract class InternalRedefinitionPlugin { + protected static final InteropLibrary INTEROP = InteropLibrary.getUncached(); + private static final String constructorName = ""; private EspressoContext context; @@ -84,6 +88,10 @@ protected KlassRef getreflectedKlassType(Object classObject) { return context.getJdwpContext().getReflectedType(classObject); } + protected Object getGuestClassInstance(KlassRef klass) { + return ((Klass) klass).mirror(); + } + protected void registerClassLoadAction(String className, ClassLoadAction action) { redefinitionPluginHandler.registerClassLoadAction(className, action); } @@ -117,7 +125,7 @@ protected void hookConstructor(KlassRef klass, MethodHook.Kind kind, MethodEntry protected void clearCollection(@Host(Collection.class) RedefineObject object, String fieldName) throws NoSuchFieldException, NoSuchMethodException { RedefineObject collectionField = object.getInstanceField(fieldName); if (collectionField != null) { - collectionField.invokeRaw("clear"); + collectionField.invoke("clear"); } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java index 60657c5cc659..46ea8cb42a33 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java @@ -31,9 +31,12 @@ public interface RedefineObject { RedefineObject fromType(String className); - RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException; + Object invoke(String name, RedefineObject... args) throws NoSuchMethodException; RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException; RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; + + Object getRawValue(); + } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java index 87a5c622113b..c69f8d710e50 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java @@ -31,6 +31,7 @@ public final class RedefintionHook implements MethodHook { private final MethodExitHook onExitHook; private final MethodEntryHook onEntryHook; private final Kind kind; + private boolean hasFired = false; public RedefintionHook(MethodEntryHook onEntryHook, Kind kind) { this.onExitHook = null; @@ -53,6 +54,7 @@ public Kind getKind() { public boolean onMethodEnter(MethodRef method, MethodVariable[] variables) { if (onEntryHook != null) { onEntryHook.onMethodEnter(method, variables); + hasFired = true; } return false; } @@ -61,7 +63,13 @@ public boolean onMethodEnter(MethodRef method, MethodVariable[] variables) { public boolean onMethodExit(MethodRef method, Object returnValue) { if (onExitHook != null) { onExitHook.onMethodExit(method, returnValue); + hasFired = true; } return false; } + + @Override + public boolean hasFired() { + return hasFired; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java index de4b5069f218..52bee0103d4a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java @@ -53,7 +53,7 @@ public CachedRedefineObject(KlassRef klass) { @Override @TruffleBoundary - public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException { + public Object invoke(String name, RedefineObject... args) throws NoSuchMethodException { StringBuilder stringBuffer = new StringBuilder(name); for (RedefineObject arg : args) { stringBuffer.append(((RedefineObjectImpl) arg).instance.get().getKlass().getNameAsString()); @@ -75,7 +75,7 @@ public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSu for (int i = 0; i < args.length; i++) { internalArgs[i] = (RedefineObjectImpl) args[i]; } - return InternalRedefinitionPlugin.createUncached(method.invokeDirect(theInstance, rawObjects(internalArgs))); + return method.invokeDirect(theInstance, rawObjects(internalArgs)); } throw new NoSuchMethodException(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java index 66de8d0f2491..efb9ddf1b114 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java @@ -25,7 +25,6 @@ import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -54,7 +53,12 @@ public Klass getKlass() { } @Override - public abstract RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException; + public Object getRawValue() { + return instance.get(); + } + + @Override + public abstract Object invoke(String name, RedefineObject... args) throws NoSuchMethodException; @SuppressWarnings("unused") public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException { @@ -64,10 +68,10 @@ public RedefineObject invokePrecise(String className, String methodName, Redefin @Override public abstract RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; - protected Method lookupMethod(String name, RedefineObject[] args) throws NoSuchMethodException { + protected Method lookupMethod(String name, RedefineObject[] args) { StaticObject theInstance = instance.get(); if (theInstance == null) { - throw new IllegalStateException("cannot invoke method on garbage collection instance"); + throw new IllegalStateException("cannot invoke method on garbage collected instance"); } Klass currentKlass = klass; @@ -77,8 +81,8 @@ protected Method lookupMethod(String name, RedefineObject[] args) throws NoSuchM if (method != null) { return method; } - ObjectKlass[] interfaces = currentKlass.getInterfaces(); - for (ObjectKlass itf : interfaces) { + Klass[] interfaces = currentKlass.getTransitiveInterfacesList(); + for (Klass itf : interfaces) { method = lookupMethod(itf, name, args); if (method != null && !method.isAbstract()) { return method; @@ -89,22 +93,68 @@ protected Method lookupMethod(String name, RedefineObject[] args) throws NoSuchM return null; } - protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args) throws NoSuchMethodException { + protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args) { for (Method declaredMethod : klassRef.getDeclaredMethods()) { if (declaredMethod.getNameAsString().equals(name)) { // match arguments boolean match = true; + Klass[] parameters = declaredMethod.resolveParameterKlasses(); if (declaredMethod.getParameterCount() == args.length) { - Klass[] parameters = declaredMethod.resolveParameterKlasses(); for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + if (parameters[i].isPrimitive()) { + match = false; + break; + } + continue; + } if (!parameters[i].isAssignableFrom((Klass) args[i].getKlass())) { match = false; break; } } } else if (declaredMethod.isVarargs()) { - // TODO - not implemented yet - throw new NoSuchMethodException("varargs lookup not implemented"); + int parameterCount = declaredMethod.getParameterCount(); + int argsCount = args.length; + + if (parameterCount == 1) { + // pure varargs + // TODO - implement this case + match = false; + } else { + // match before varargs param + for (int i = 0; i < parameterCount - 1; i++) { + if (args[i] == null) { + if (parameters[i].isPrimitive()) { + match = false; + break; + } + continue; + } + if (!parameters[i].isAssignableFrom((Klass) args[i].getKlass())) { + match = false; + break; + } + } + // match varargs + Klass varargsParam = parameters[parameterCount - 1]; + for (int i = parameterCount - 1; i < argsCount; i++) { + if (args[i] == null) { + if (varargsParam.isPrimitive()) { + match = false; + break; + } + continue; + } + if (varargsParam.isAssignableFrom((Klass) args[i].getKlass())) { + match = false; + break; + } + } + } + } else { + // arguments didn't match + match = false; } if (match) { return declaredMethod; @@ -117,7 +167,7 @@ protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args protected Object[] rawObjects(RedefineObjectImpl[] args) { Object[] result = new Object[args.length]; for (int i = 0; i < args.length; i++) { - result[i] = args[i].instance; + result[i] = args[i].instance.get(); } return result; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java index 251b354f5101..4b5729388ddc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java @@ -35,14 +35,14 @@ public UncachedRedefineObject(StaticObject object) { } @Override - public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException { + public Object invoke(String name, RedefineObject... args) throws NoSuchMethodException { Method method = lookupMethod(name, args); if (method != null) { RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; for (int i = 0; i < args.length; i++) { internalArgs[i] = (RedefineObjectImpl) args[i]; } - return InternalRedefinitionPlugin.createUncached(method.invokeDirect(instance, rawObjects(internalArgs))); + return method.invokeDirect(instance.get(), rawObjects(internalArgs)); } throw new NoSuchMethodException(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index 0e4bbcb18d8a..267601e63398 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -29,7 +29,6 @@ import java.util.Map; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.espresso.jdwp.api.KlassRef; @@ -43,8 +42,6 @@ public class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { - public static final InteropLibrary INTEROP = InteropLibrary.getUncached(); - private static final String PROXY_GENERATOR_CLASS = "sun.misc.ProxyGenerator"; private static final String GENERATOR_METHOD = "generateProxyClass"; private static final String GENERATOR_METHOD_SIG = "(Ljava/lang/String;[Ljava/lang/Class;I)[B"; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java index 06d30a81c807..b1e1495b673e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java @@ -24,7 +24,6 @@ import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodHook; -import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; @@ -32,26 +31,30 @@ import java.util.ArrayList; -// TODO - current state doesn't reflect changes to apps properly -// TODO - this needs more thorough integration to Micronaut public class MicronautPlugin extends InternalRedefinitionPlugin { private static final String BEAN_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractBeanDefinitionReference"; private static final String EXECUTION_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractExecutableMethod"; - private static final String DEFAULT_BEAN_CONTEXT_ACTIVATION_NAME = "io.micronaut.context.DefaultBeanContext"; - private static final String EMBEDDED_APPLICATION_CLASS_NAME = "io.micronaut.runtime.EmbeddedApplication"; - private static final String BEAN_LOCATOR_CLASS_NAME = "io.micronaut.context.BeanLocator"; + private static final String MICRONAUT_CLASS = "io.micronaut.runtime.Micronaut"; + private static final String ROUTING_IN_BOUND_HANDLER = "io.micronaut.http.server.netty.RoutingInBoundHandler"; + + private static final String RUN = "run"; + private static final String RUN_SIG = "([Ljava/lang/Class;[Ljava/lang/String;)Lio/micronaut/context/ApplicationContext;"; + private static final String STOP = "stop"; + private static final String CHANNEL_READ_0 = "channelRead0"; + private static final String CHANNEL_READ_0_SIG = "(Lio/netty/channel/ChannelHandlerContext;Lio/micronaut/http/HttpRequest;)V"; - private static final String START_METHOD_NAME = "start"; - private static final String START_METHOD_SIGNATURE = "()Lio/micronaut/context/BeanContext;"; private ArrayList clinitRerunTypes; private boolean needsBeanRefresh; // the default bean context - private RedefineObject defaultBeanContext; - // the embedded application instance - private RedefineObject embeddedApplicationType; + private RedefineObject micronautContext; + + private RedefineObject[] runArgs; + private RedefineObject micronautClassInstance; + + private KlassRef rountingHandler; @Override public String getName() { @@ -60,16 +63,29 @@ public String getName() { @Override public TriggerClass[] getTriggerClasses() { - ArrayList triggers = new ArrayList<>(3); - triggers.add(new TriggerClass(DEFAULT_BEAN_CONTEXT_ACTIVATION_NAME, this, klass -> { - // default bean class loaded, so register a listener on the start method - // for fetching the context object we need on redefinitions later - hookMethodExit(klass, new MethodLocator(START_METHOD_NAME, START_METHOD_SIGNATURE), MethodHook.Kind.ONE_TIME, (method, returnValue) -> { - defaultBeanContext = InternalRedefinitionPlugin.createCached(returnValue); + ArrayList triggers = new ArrayList<>(4); + triggers.add(new TriggerClass(MICRONAUT_CLASS, this, klass -> { + // we need the application context when reloading + hookMethodExit(klass, new MethodLocator(RUN, RUN_SIG), MethodHook.Kind.INDEFINITE, (method, returnValue) -> { + micronautContext = InternalRedefinitionPlugin.createCached(returnValue); + }); + // we need the run arguments for re-starting a fresh context + hookMethodEntry(klass, new MethodLocator(RUN, RUN_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { + micronautClassInstance = InternalRedefinitionPlugin.createCached(klass); + // collect run arguments + runArgs = new RedefineObject[2]; + // array of j.l.Class instances + runArgs[0] = InternalRedefinitionPlugin.createUncached(variables[0].getValue()); + // array of String args + runArgs[1] = InternalRedefinitionPlugin.createUncached(variables[1].getValue()); }); })); triggers.add(new TriggerClass(BEAN_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); triggers.add(new TriggerClass(EXECUTION_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); + triggers.add(new TriggerClass(ROUTING_IN_BOUND_HANDLER, this, klass -> { + rountingHandler = klass; + })); + return triggers.toArray(new TriggerClass[triggers.size()]); } @@ -83,9 +99,8 @@ private synchronized void addClinitRerunKlass(KlassRef klass) { @Override public boolean reRunClinit(KlassRef klass, boolean changed) { if (changed) { - KlassRef superClass = klass.getSuperClass(); for (KlassRef rerunType : clinitRerunTypes) { - if (rerunType == superClass) { + if (rerunType.isAssignable(klass)) { needsBeanRefresh = true; return true; } @@ -96,41 +111,20 @@ public boolean reRunClinit(KlassRef klass, boolean changed) { @Override public void postClassRedefinition(KlassRef[] changedKlasses) { - if (needsBeanRefresh && defaultBeanContext.notNull()) { - try { - // clear bean caches - flushBeanCaches(); - // fetch needed class types and instances one time - if (embeddedApplicationType == null) { - embeddedApplicationType = defaultBeanContext.fromType(EMBEDDED_APPLICATION_CLASS_NAME); + if (needsBeanRefresh && micronautContext != null) { + // OK, simple HotSwap is not enough, so register a reload hook + // in the HTTP pipeline that restarts the context on the next + // request. + hookMethodEntry(rountingHandler, new MethodLocator(CHANNEL_READ_0, CHANNEL_READ_0_SIG), MethodHook.Kind.ONE_TIME, (method, variables) -> { + try { + // restart Micronaut application context + micronautContext.invoke(STOP); + micronautClassInstance.invoke("run", runArgs); + } catch (Throwable e) { + e.printStackTrace(); } - defaultBeanContext.invokeRaw("startEnvironment"); - - // force a bean re-scan: - defaultBeanContext.invokeRaw("readAllBeanConfigurations"); - defaultBeanContext.invokeRaw("readAllBeanDefinitionClasses"); - - // re-wire beans for the application - defaultBeanContext.invokePrecise(BEAN_LOCATOR_CLASS_NAME, "findBean", embeddedApplicationType); - } catch (Throwable ex) { - JDWPLogger.log("Failed to reload Micronaut beans due to %s", JDWPLogger.LogLevel.ALL, ex.getMessage()); - } + }); } needsBeanRefresh = false; } - - private void flushBeanCaches() { - try { - clearCollection(defaultBeanContext, "singletonObjects"); - clearCollection(defaultBeanContext, "scopedProxies"); - clearCollection(defaultBeanContext, "beanDefinitionsClasses"); - clearCollection(defaultBeanContext, "containsBeanCache"); - clearCollection(defaultBeanContext, "initializedObjectsByType"); - clearCollection(defaultBeanContext, "beanConcreteCandidateCache"); - clearCollection(defaultBeanContext, "beanCandidateCache"); - clearCollection(defaultBeanContext, "beanIndex"); - } catch (NoSuchFieldException | NoSuchMethodException ex) { - JDWPLogger.log("Failed to flush Micronaut caches due to %s", JDWPLogger.LogLevel.ALL, ex.getMessage()); - } - } } From 1b7d1fd5a07d1105923240c6b7a524d211922c04 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 18 Mar 2021 08:40:24 +0100 Subject: [PATCH 004/290] rerun class initializers during HotSwap in a dedicated thread and mark it as a system thread to avoid suspension --- .../espresso/jdwp/api/JDWPContext.java | 2 + .../jdwp/impl/DebuggerController.java | 8 ++- .../redefinition/ClassRedefinition.java | 2 +- .../espresso/runtime/JDWPContextImpl.java | 59 +++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) 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 feb7d7493361..c5c1fef01064 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 @@ -490,4 +490,6 @@ public interface JDWPContext { * @return the nearest instrumentable node */ Node getInstrumentableNode(Node node); + + boolean isSystemThread(Thread hostThread); } 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 4146c6a1a328..b15b5d612609 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 @@ -787,7 +787,13 @@ private class SuspendedCallbackImpl implements SuspendedCallback { @Override public void onSuspend(SuspendedEvent event) { - Object currentThread = getContext().asGuestThread(Thread.currentThread()); + Thread hostThread = Thread.currentThread(); + if (context.isSystemThread(hostThread)) { + // always allow VM threads to run guest code without + // the risk of being suspended + return; + } + Object currentThread = getContext().asGuestThread(hostThread); JDWPLogger.log("Suspended at: %s in thread: %s", JDWPLogger.LogLevel.STEPPING, event.getSourceSection().toString(), getThreadName(currentThread)); SteppingInfo steppingInfo = commandRequestIds.remove(currentThread); if (steppingInfo != null) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index de4ec17d3fb1..1cd1eff125b8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -664,7 +664,7 @@ private void doRedefineClass(ChangePacket packet, List refreshSubCl } oldKlass.redefineClass(packet, refreshSubClasses, ids); if (redefineListener.rerunClinit(oldKlass, packet.detectedChange.clinitChanged())) { - oldKlass.reRunClinit(); + context.getJdwpContext().rerunclinit(oldKlass); } } 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 ae176ec72894..2ec7a333c972 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 @@ -29,6 +29,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.regex.Matcher; import com.oracle.truffle.api.TruffleLanguage; @@ -85,6 +87,8 @@ public final class JDWPContextImpl implements JDWPContext { private final ClassRedefinition classRedefinition; private InnerClassRedefiner innerClassRedefiner; private final RedefinitionPluginHandler redefinitionPluginHandler; + private Thread reloaderThread; + private final BlockingQueue queue = new ArrayBlockingQueue<>(128); public JDWPContextImpl(EspressoContext context) { this.context = context; @@ -93,6 +97,7 @@ public JDWPContextImpl(EspressoContext context) { this.innerClassRedefiner = new InnerClassRedefiner(context); this.redefinitionPluginHandler = RedefinitionPluginHandler.create(context); this.classRedefinition = new ClassRedefinition(context, ids, redefinitionPluginHandler); + initializeReloaderThread(); } public VMListener jdwpInit(TruffleLanguage.Env env, Object mainThread) { @@ -682,6 +687,47 @@ public Node getInstrumentableNode(Node node) { return null; } + @Override + public boolean isSystemThread(Thread hostThread) { + return hostThread == reloaderThread; + } + + private void initializeReloaderThread() { + reloaderThread = new Thread(new Runnable() { + @Override + public void run() { + while (!reloaderThread.isInterrupted()) { + try { + queue.take().fire(); + } catch (InterruptedException e) { + reloaderThread.interrupt(); + } catch (Throwable t) { + // while rerunning class initializers + // in the guest, some anomalies are to + // be expected. Treat those as non-fatal + // TODO - add logging + } + } + } + }, "reloader"); + reloaderThread.start(); + } + + public void rerunclinit(ObjectKlass oldKlass) { + queue.add(new ReloadingAction(oldKlass)); + } + + public void ensureReloadingDone() { + while (!queue.isEmpty()) { + // poll at intervals + try { + Thread.sleep(10); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + @Override public synchronized int redefineClasses(List redefineInfos) { try { @@ -813,4 +859,17 @@ public int compare(ObjectKlass k1, ObjectKlass k2) { return 0; } } + + private class ReloadingAction { + private ObjectKlass klass; + + private ReloadingAction(ObjectKlass klass) { + this.klass = klass; + } + + private void fire() { + System.out.println("running clinit action for klass: " + klass); + klass.reRunClinit(); + } + } } From 6fd2ea9df533112cae7bed7000fa0a5021447d3b Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 6 Apr 2021 08:20:49 +0200 Subject: [PATCH 005/290] Add API for registering post HotSwap actions. Static class re-init now checks hierarchy when deciding whether to re-run clinit --- .../espresso/hotswap/EspressoHotSwap.java | 6 +++++ .../espresso/hotswap/HotSwapHandler.java | 25 ++++++++++++++++--- .../espresso/runtime/JDWPContextImpl.java | 1 - 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java index c8f11f5f23c3..7ddde00f2b0b 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -57,6 +57,12 @@ public static void registerPlugin(HotSwapPlugin plugin) { } } + public static void registerPostHotSwapAction(HotSwapAction action) { + if (handler != null) { + handler.registerPostHotSwapAction(action); + } + } + public static void registerHotSwapAction(Class klass, HotSwapAction action) { if (handler != null) { handler.registerHotSwapAction(klass, action); diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java index 4a4bee7ff740..1997b2f7fb12 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java @@ -40,9 +40,11 @@ */ package com.oracle.truffle.espresso.hotswap; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -52,6 +54,7 @@ final class HotSwapHandler { private final Set plugins = Collections.synchronizedSet(new HashSet<>()); private final Map, Set> hotSwapActions = new HashMap<>(); + private final List postHotSwapActions = Collections.synchronizedList(new ArrayList<>()); private final Map, Boolean> staticInitializerHotSwap = new HashMap<>(); private HotSwapHandler() { @@ -77,12 +80,16 @@ public void registerHotSwapAction(Class klass, HotSwapAction action) { hotSwapActions.get(klass).add(action); } + public void registerPostHotSwapAction(HotSwapAction action) { + postHotSwapActions.add(action); + } + public void registerStaticClassInitHotSwap(Class klass, boolean onChange) { - if (!staticInitializerHotSwap.containsKey(klass)) { - staticInitializerHotSwap.put(klass, onChange); - } else if (!onChange) { + if (!staticInitializerHotSwap.containsKey(klass)) { + staticInitializerHotSwap.put(klass, onChange); + } else if (!onChange) { staticInitializerHotSwap.put(klass, false); - } + } } @SuppressWarnings("unused") @@ -96,6 +103,8 @@ public void postHotSwap(Class[] changedClasses) { for (HotSwapPlugin plugin : plugins) { plugin.postHotSwap(); } + // fire all registered post HotSwap actions + postHotSwapActions.forEach(HotSwapAction::onHotSwap); } @SuppressWarnings("unused") @@ -103,6 +112,14 @@ public boolean rerunClassInit(Class klass, boolean changed) { if (staticInitializerHotSwap.containsKey(klass)) { boolean onlyOnChange = staticInitializerHotSwap.get(klass); return !onlyOnChange || changed; + } else { + // check class hierarchy + for (Map.Entry, Boolean> entry : staticInitializerHotSwap.entrySet()) { + Class key = entry.getKey(); + if (key.isAssignableFrom(klass)) { + return !entry.getValue() || changed; + } + } } return false; } 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 2ec7a333c972..619d403f138f 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 @@ -868,7 +868,6 @@ private ReloadingAction(ObjectKlass klass) { } private void fire() { - System.out.println("running clinit action for klass: " + klass); klass.reRunClinit(); } } From 9adc865f6150b0d650ffdd08b45f6f3db5e023a7 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 7 Apr 2021 14:30:10 +0200 Subject: [PATCH 006/290] remove internal Micronaut plugin --- .../plugins/micronaut/MicronautPlugin.java | 130 ------------------ 1 file changed, 130 deletions(-) delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java deleted file mode 100644 index b1e1495b673e..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.micronaut; - -import com.oracle.truffle.espresso.jdwp.api.KlassRef; -import com.oracle.truffle.espresso.jdwp.api.MethodHook; -import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; -import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; -import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; -import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; - -import java.util.ArrayList; - -public class MicronautPlugin extends InternalRedefinitionPlugin { - - private static final String BEAN_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractBeanDefinitionReference"; - private static final String EXECUTION_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractExecutableMethod"; - private static final String MICRONAUT_CLASS = "io.micronaut.runtime.Micronaut"; - private static final String ROUTING_IN_BOUND_HANDLER = "io.micronaut.http.server.netty.RoutingInBoundHandler"; - - private static final String RUN = "run"; - private static final String RUN_SIG = "([Ljava/lang/Class;[Ljava/lang/String;)Lio/micronaut/context/ApplicationContext;"; - private static final String STOP = "stop"; - private static final String CHANNEL_READ_0 = "channelRead0"; - private static final String CHANNEL_READ_0_SIG = "(Lio/netty/channel/ChannelHandlerContext;Lio/micronaut/http/HttpRequest;)V"; - - - private ArrayList clinitRerunTypes; - private boolean needsBeanRefresh; - - // the default bean context - private RedefineObject micronautContext; - - private RedefineObject[] runArgs; - private RedefineObject micronautClassInstance; - - private KlassRef rountingHandler; - - @Override - public String getName() { - return "Micronaut Reloading Plugin"; - } - - @Override - public TriggerClass[] getTriggerClasses() { - ArrayList triggers = new ArrayList<>(4); - triggers.add(new TriggerClass(MICRONAUT_CLASS, this, klass -> { - // we need the application context when reloading - hookMethodExit(klass, new MethodLocator(RUN, RUN_SIG), MethodHook.Kind.INDEFINITE, (method, returnValue) -> { - micronautContext = InternalRedefinitionPlugin.createCached(returnValue); - }); - // we need the run arguments for re-starting a fresh context - hookMethodEntry(klass, new MethodLocator(RUN, RUN_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { - micronautClassInstance = InternalRedefinitionPlugin.createCached(klass); - // collect run arguments - runArgs = new RedefineObject[2]; - // array of j.l.Class instances - runArgs[0] = InternalRedefinitionPlugin.createUncached(variables[0].getValue()); - // array of String args - runArgs[1] = InternalRedefinitionPlugin.createUncached(variables[1].getValue()); - }); - })); - triggers.add(new TriggerClass(BEAN_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); - triggers.add(new TriggerClass(EXECUTION_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); - triggers.add(new TriggerClass(ROUTING_IN_BOUND_HANDLER, this, klass -> { - rountingHandler = klass; - })); - - return triggers.toArray(new TriggerClass[triggers.size()]); - } - - private synchronized void addClinitRerunKlass(KlassRef klass) { - if (clinitRerunTypes == null) { - clinitRerunTypes = new ArrayList<>(1); - } - clinitRerunTypes.add(klass); - } - - @Override - public boolean reRunClinit(KlassRef klass, boolean changed) { - if (changed) { - for (KlassRef rerunType : clinitRerunTypes) { - if (rerunType.isAssignable(klass)) { - needsBeanRefresh = true; - return true; - } - } - } - return false; - } - - @Override - public void postClassRedefinition(KlassRef[] changedKlasses) { - if (needsBeanRefresh && micronautContext != null) { - // OK, simple HotSwap is not enough, so register a reload hook - // in the HTTP pipeline that restarts the context on the next - // request. - hookMethodEntry(rountingHandler, new MethodLocator(CHANNEL_READ_0, CHANNEL_READ_0_SIG), MethodHook.Kind.ONE_TIME, (method, variables) -> { - try { - // restart Micronaut application context - micronautContext.invoke(STOP); - micronautClassInstance.invoke("run", runArgs); - } catch (Throwable e) { - e.printStackTrace(); - } - }); - } - needsBeanRefresh = false; - } -} From f1b35de5e20ab9de816f984934fd74cbaf8640c9 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 7 Apr 2021 14:31:47 +0200 Subject: [PATCH 007/290] add new internal JDK introspector and bean cache flushing plugin --- ...ion.plugins.api.InternalRedefinitionPlugin | 2 +- .../jdkcaches/JDKCacheRedefinitionPlugin.java | 85 +++++++++++++++++++ .../jdkproxy/JDKProxyRedefinitionPlugin.java | 2 +- 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin index a03c21cb2019..46bb33322bc8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin +++ b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin @@ -1,2 +1,2 @@ -com.oracle.truffle.espresso.redefinition.plugins.micronaut.MicronautPlugin com.oracle.truffle.espresso.redefinition.plugins.jdkproxy.JDKProxyRedefinitionPlugin +com.oracle.truffle.espresso.redefinition.plugins.jdkcaches.JDKCacheRedefinitionPlugin diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java new file mode 100644 index 000000000000..ccb4effcce84 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.jdkcaches; + +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.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class JDKCacheRedefinitionPlugin extends InternalRedefinitionPlugin { + + public static final String INTROSPECTOR_CLASS = "java.beans.Introspector"; + public static final String FLUSH_CACHES_METHOD = "flushFromCaches"; + public static final String FLUSH_CACHES_SIG = "(Ljava/lang/Class;)V"; + private MethodRef flushFromCachesMethod; + + public static final String THREAD_GROUP_CONTEXT = "java.beans.ThreadGroupContext"; + public static final String REMOVE_BEAN_INFO = "removeBeanInfo"; + private List threadGroupContext = Collections.synchronizedList(new ArrayList<>(1)); + + @Override + public String getName() { + return "JDK Cache Flushing Plugin"; + } + + @Override + public TriggerClass[] getTriggerClasses() { + TriggerClass[] triggerClasses = new TriggerClass[2]; + triggerClasses[0] = new TriggerClass(INTROSPECTOR_CLASS, this, klass -> { + hookMethodEntry(klass, new MethodLocator(FLUSH_CACHES_METHOD, FLUSH_CACHES_SIG), MethodHook.Kind.ONE_TIME, + ((method, variables) -> flushFromCachesMethod = method)); + }); + triggerClasses[1] = new TriggerClass(THREAD_GROUP_CONTEXT, this, klass -> { + hookConstructor(klass, MethodHook.Kind.INDEFINITE, ((method, variables) -> { + threadGroupContext.add(InternalRedefinitionPlugin.createCached(variables[0].getValue())); + })); + }); + return triggerClasses; + } + + @Override + public void postClassRedefinition(KlassRef[] changedKlasses) { + for (KlassRef changedKlass : changedKlasses) { + Object guestKlass = getGuestClassInstance(changedKlass); + if (flushFromCachesMethod != null) { + flushFromCachesMethod.invokeMethod(null, new Object[]{guestKlass}); + } + for (RedefineObject context : threadGroupContext) { + try { + context.invoke(REMOVE_BEAN_INFO, InternalRedefinitionPlugin.createUncached(guestKlass)); + } catch (NoSuchMethodException e) { + // TODO - add logging + } + } + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index 267601e63398..1e4eed94d824 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -40,7 +40,7 @@ import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; -public class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { +public final class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { private static final String PROXY_GENERATOR_CLASS = "sun.misc.ProxyGenerator"; private static final String GENERATOR_METHOD = "generateProxyClass"; From 631166a5f860b4c617dfc27717fabe1e9e37659f Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 7 Apr 2021 14:33:50 +0200 Subject: [PATCH 008/290] Espresso HotSwap plugin API updates that allow callbacks to fire on various redefinition changes --- .../espresso/hotswap/EspressoHotSwap.java | 4 +-- .../espresso/hotswap/HotSwapAction.java | 2 +- .../espresso/hotswap/HotSwapHandler.java | 29 +++++++++++++++---- .../espresso/hotswap/HotSwapPlugin.java | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java index 7ddde00f2b0b..acc24b78cbc0 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -69,9 +69,9 @@ public static void registerHotSwapAction(Class klass, HotSwapAction action) { } } - public static void registerClassInitHotSwap(Class klass, boolean onChange) { + public static void registerClassInitHotSwap(Class klass, boolean onChange, HotSwapAction callback) { if (handler != null) { - handler.registerStaticClassInitHotSwap(klass, onChange); + handler.registerStaticClassInitHotSwap(klass, onChange, callback); } } } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java index 1e43bd871e68..1a81db5905af 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java @@ -41,5 +41,5 @@ package com.oracle.truffle.espresso.hotswap; public interface HotSwapAction { - void onHotSwap(); + void fire(); } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java index 1997b2f7fb12..779335abe068 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java @@ -56,6 +56,7 @@ final class HotSwapHandler { private final Map, Set> hotSwapActions = new HashMap<>(); private final List postHotSwapActions = Collections.synchronizedList(new ArrayList<>()); private final Map, Boolean> staticInitializerHotSwap = new HashMap<>(); + private final Map, List> staticReInitCallBacks = new HashMap<>(); private HotSwapHandler() { } @@ -84,12 +85,20 @@ public void registerPostHotSwapAction(HotSwapAction action) { postHotSwapActions.add(action); } - public void registerStaticClassInitHotSwap(Class klass, boolean onChange) { + public void registerStaticClassInitHotSwap(Class klass, boolean onChange, HotSwapAction callback) { if (!staticInitializerHotSwap.containsKey(klass)) { staticInitializerHotSwap.put(klass, onChange); } else if (!onChange) { staticInitializerHotSwap.put(klass, false); } + if (callback != null) { + List reInitCallbacks = staticReInitCallBacks.get(klass); + if (reInitCallbacks == null) { + reInitCallbacks = new ArrayList<>(1); + staticReInitCallBacks.put(klass, reInitCallbacks); + } + reInitCallbacks.add(callback); + } } @SuppressWarnings("unused") @@ -97,27 +106,35 @@ public void postHotSwap(Class[] changedClasses) { // fire all registered HotSwap actions for (Class klass : changedClasses) { Set actions = hotSwapActions.getOrDefault(klass, Collections.emptySet()); - actions.forEach(HotSwapAction::onHotSwap); + actions.forEach(HotSwapAction::fire); } // fire a generic HotSwap plugin listener for (HotSwapPlugin plugin : plugins) { - plugin.postHotSwap(); + plugin.postHotSwap(changedClasses); } // fire all registered post HotSwap actions - postHotSwapActions.forEach(HotSwapAction::onHotSwap); + postHotSwapActions.forEach(HotSwapAction::fire); } @SuppressWarnings("unused") public boolean rerunClassInit(Class klass, boolean changed) { if (staticInitializerHotSwap.containsKey(klass)) { boolean onlyOnChange = staticInitializerHotSwap.get(klass); - return !onlyOnChange || changed; + boolean rerun = !onlyOnChange || changed; + if (rerun) { + staticReInitCallBacks.getOrDefault(klass, Collections.emptyList()).forEach(HotSwapAction::fire); + } + return rerun; } else { // check class hierarchy for (Map.Entry, Boolean> entry : staticInitializerHotSwap.entrySet()) { Class key = entry.getKey(); if (key.isAssignableFrom(klass)) { - return !entry.getValue() || changed; + boolean rerun = !entry.getValue() || changed; + if (rerun) { + staticReInitCallBacks.getOrDefault(key, Collections.emptyList()).forEach(HotSwapAction::fire); + } + return rerun; } } } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java index edfd48170d98..690d5805681a 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java @@ -43,5 +43,5 @@ public interface HotSwapPlugin { String getName(); - void postHotSwap(); + void postHotSwap(Class[] changedClasses); } From 8fe19b03598daf875a7054e840ea8e8911b77a9d Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Fri, 12 Mar 2021 14:44:04 +0100 Subject: [PATCH 009/290] Initial internal and external redefinition APIs --- .../espresso/jdwp/api/JDWPContext.java | 2 +- .../truffle/espresso/jdwp/api/MethodHook.java | 41 +++++ .../truffle/espresso/jdwp/api/MethodRef.java | 8 +- .../espresso/jdwp/api/MethodVariable.java | 41 +++++ .../truffle/espresso/jdwp/api/VMListener.java | 23 ++- .../jdwp/impl/DebuggerConnection.java | 1 + .../espresso/jdwp/impl/EmptyListener.java | 5 + .../truffle/espresso/jdwp/impl/JDWP.java | 6 +- .../jdwp/impl/MethodBreakpointInfo.java | 31 ++-- .../jdwp/impl/RequestedJDWPEvents.java | 4 +- .../jdwp/impl/VMEventListenerImpl.java | 71 +++++++- .../espresso/hotswap/EspressoHotSwap.java | 71 ++++++++ .../espresso/hotswap/HotSwapAction.java | 45 +++++ .../espresso/hotswap/HotSwapHandler.java | 109 ++++++++++++ .../espresso/hotswap/HotSwapPlugin.java | 47 +++++ ...ion.plugins.api.InternalRedefinitionPlugin | 2 + .../espresso/impl/ClassRegistries.java | 17 +- .../truffle/espresso/impl/ClassRegistry.java | 2 + .../espresso/impl/ConstantPoolPatcher.java | 1 + .../truffle/espresso/impl/LinkedKlass.java | 2 +- .../oracle/truffle/espresso/impl/Method.java | 65 +++---- .../truffle/espresso/impl/ObjectKlass.java | 8 + .../truffle/espresso/impl/ParserKlass.java | 6 +- .../truffle/espresso/nodes/BytecodeNode.java | 15 +- .../nodes/interop/InvokeEspressoNode.java | 2 +- .../nodes/methodhandle/MHLinkToNode.java | 2 +- .../quick/invoke/InvokeInterfaceNode.java | 2 +- .../nodes/quick/invoke/InvokeSpecialNode.java | 2 +- .../nodes/quick/invoke/InvokeStaticNode.java | 2 +- .../nodes/quick/invoke/InvokeVirtualNode.java | 2 +- .../{impl => redefinition}/ChangePacket.java | 6 +- .../{impl => redefinition}/ClassInfo.java | 9 +- .../redefinition/ClassLoadListener.java | 7 + .../ClassRedefinition.java | 60 +++++-- .../DefineKlassListener.java | 4 +- .../DetectedChange.java | 25 ++- .../HotSwapClassInfo.java | 3 +- .../ImmutableClassInfo.java | 3 +- .../InnerClassRedefiner.java | 50 +++--- .../RedefintionNotSupportedException.java | 8 +- .../plugins/api/ClassLoadAction.java} | 10 +- .../api/InternalRedefinitionPlugin.java | 123 +++++++++++++ .../plugins/api/MethodEntryHook.java | 30 ++++ .../plugins/api/MethodExitHook.java | 29 +++ .../plugins/api/MethodLocator.java | 41 +++++ .../plugins/api/RedefineObject.java | 39 +++++ .../plugins/api/RedefinitionAction.java | 7 + .../plugins/api/RedefintionHook.java | 67 +++++++ .../plugins/api/TriggerClass.java | 50 ++++++ .../plugins/api/TriggerClassHook.java | 29 +++ .../plugins/impl/CachedRedefineObject.java | 130 ++++++++++++++ .../plugins/impl/ExternalPluginHandler.java | 78 +++++++++ .../plugins/impl/RedefineListener.java | 36 ++++ .../plugins/impl/RedefineObjectImpl.java | 138 +++++++++++++++ .../impl/RedefinitionPluginHandler.java | 165 ++++++++++++++++++ .../plugins/impl/UncachedRedefineObject.java | 67 +++++++ .../jdkproxy/JDKProxyRedefinitionPlugin.java | 159 +++++++++++++++++ .../plugins/micronaut/MicronautPlugin.java | 136 +++++++++++++++ .../espresso/runtime/EspressoContext.java | 13 +- .../espresso/runtime/JDWPContextImpl.java | 141 +++++++++------ ...uffle_espresso_hotswap_HotSwapHandler.java | 40 +++++ ..._sun_reflect_NativeMethodAccessorImpl.java | 2 +- 62 files changed, 2148 insertions(+), 192 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVariable.java create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/ChangePacket.java (91%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/ClassInfo.java (95%) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/ClassRedefinition.java (92%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/DefineKlassListener.java (91%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/DetectedChange.java (76%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/HotSwapClassInfo.java (98%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/ImmutableClassInfo.java (97%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/InnerClassRedefiner.java (91%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{impl => redefinition}/RedefintionNotSupportedException.java (83%) rename espresso/src/{com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodBreakpoint.java => com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/ClassLoadAction.java} (80%) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java 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 49e8852444da..feb7d7493361 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 @@ -466,7 +466,7 @@ public interface JDWPContext { * @param redefineInfos the information about the original class and the new class bytes * @return 0 on success or the appropriate {@link ErrorCodes} if an error occur */ - int redefineClasses(RedefineInfo[] redefineInfos); + int redefineClasses(List redefineInfos); /** * Exit all monitors that was entered by the frame. diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java new file mode 100644 index 000000000000..d7420f5ed6d9 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020, 2021, 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 MethodHook { + + enum Kind { + ONE_TIME, + INDEFINITE + } + + default int getRequestId() { + return -1; + } + + Kind getKind(); + + boolean onMethodEnter(MethodRef method, MethodVariable[] variables); + + boolean onMethodExit(MethodRef method, Object returnValue); +} 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 1e86414e7ce0..f2cdcce9172a 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 @@ -179,28 +179,28 @@ public interface MethodRef { * * @return array of method breakpoint info */ - MethodBreakpoint[] getMethodBreakpointInfos(); + MethodHook[] getMethodHooks(); /** * Add a new method breakpoint with the given info on this method. * * @param info the info that describes the breakpoint */ - void addMethodBreakpointInfo(MethodBreakpoint info); + void addMethodHook(MethodHook info); /** * Remove a method breakpoint with the given info on this method. * * @param requestId the ID for the request that set the breakpoint */ - void removeMethodBreakpointInfo(int requestId); + void removedMethodHook(int requestId); /** * Determines if there are any breakpoints set on this method. * * @return true if this method has any breakpoints, false otherwise */ - boolean hasActiveBreakpoint(); + boolean hasActiveHook(); /** * Determine if this method is obsolete. A method is obsolete if it has been replaced by a diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVariable.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVariable.java new file mode 100644 index 000000000000..394b8d31a05b --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVariable.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, 2021, 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 final class MethodVariable { + private final String identifier; + private final Object value; + + public MethodVariable(String identifier, Object value) { + this.identifier = identifier; + this.value = value; + } + + public String getIdentifier() { + return identifier; + } + + public Object getValue() { + return value; + } +} 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 064cde2934a4..5adc987ad0ba 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 @@ -70,12 +70,25 @@ public interface VMListener { boolean onFieldAccess(FieldRef field, Object receiver); /** - * This method will be called when a method is about to return. If will determine if there is an - * active method exit breakpoint that will be triggered. + * This method will be called when a method is entered iff there is an active + * {@link MethodHook}. Returns true if the method has an active method exit breakpoint that + * should be triggered. * - * @param method the field - * @param returnValue owner of the field - * @return true if a breakpoint should be hit due to the modification + * @param method the method + * @param scope the {@link com.oracle.truffle.api.interop.InteropLibrary} object representing + * local variables in scope + * @return true a breakpoint should be hit on method entry + */ + boolean onMethodEntry(MethodRef method, Object scope); + + /** + * This method will be called when a method is about to return iff there is an active + * {@link MethodHook}. Returns true if the method has an active method exit breakpoint that + * should be triggered. + * + * @param method the method + * @param returnValue the return value + * @return true if a breakpoint should be hit on method exit */ boolean onMethodReturn(MethodRef method, Object returnValue); diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java index ec3afdffa2b6..797adcfa36eb 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java @@ -602,6 +602,7 @@ private void processPacket(Packet packet) { JDWPLogger.throwing(JDWPLogger.LogLevel.ALL, t); PacketStream reply = new PacketStream().replyPacket().id(packet.id); reply.errorCode(ErrorCodes.INTERNAL); + t.printStackTrace(); handleReply(packet, new CommandResult(reply)); } finally { if (entered) { diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/EmptyListener.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/EmptyListener.java index 58479a3e8a3e..ddc38d59b30d 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/EmptyListener.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/EmptyListener.java @@ -59,6 +59,11 @@ public boolean onFieldAccess(FieldRef field, Object receiver) { return false; } + @Override + public boolean onMethodEntry(MethodRef method, Object scope) { + return false; + } + @Override public boolean onMethodReturn(MethodRef method, Object returnValue) { return false; 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 615c6cc4fc4a..c8dcb8512a2a 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 @@ -387,7 +387,7 @@ static CommandResult createReply(Packet packet, JDWPContext context) { PacketStream reply = new PacketStream().replyPacket().id(packet.id); int classes = input.readInt(); JDWPLogger.log("Request to redefine %d classes received", JDWPLogger.LogLevel.REDEFINE, classes); - RedefineInfo[] redefineInfos = new RedefineInfo[classes]; + List redefineInfos = new ArrayList<>(classes); for (int i = 0; i < classes; i++) { KlassRef klass = null; long refTypeId = input.readLong(); @@ -408,7 +408,7 @@ static CommandResult createReply(Packet packet, JDWPContext context) { int byteLength = input.readInt(); byte[] classBytes = input.readByteArray(byteLength); - redefineInfos[i] = new RedefineInfo(klass, classBytes); + redefineInfos.add(new RedefineInfo(klass, classBytes)); } int errorCode = context.redefineClasses(redefineInfos); @@ -2990,6 +2990,8 @@ private static void writeMethodResult(PacketStream reply, JDWPContext context, T JDWPLogger.log("Internal Espresso error: %s", JDWPLogger.LogLevel.ALL, t); JDWPLogger.throwing(JDWPLogger.LogLevel.ALL, t); reply.errorCode(ErrorCodes.INTERNAL); + System.out.println("ERROR!"); + t.printStackTrace(); } } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/MethodBreakpointInfo.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/MethodBreakpointInfo.java index fa6c813b0576..d842b174dc48 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/MethodBreakpointInfo.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/MethodBreakpointInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, 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 @@ -22,12 +22,13 @@ */ package com.oracle.truffle.espresso.jdwp.impl; -import com.oracle.truffle.espresso.jdwp.api.MethodBreakpoint; -import com.oracle.truffle.espresso.jdwp.api.MethodRef; - import java.util.Arrays; -public class MethodBreakpointInfo extends AbstractBreakpointInfo implements MethodBreakpoint { +import com.oracle.truffle.espresso.jdwp.api.MethodHook; +import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVariable; + +public final class MethodBreakpointInfo extends AbstractBreakpointInfo implements MethodHook { private MethodRef[] methods = new MethodRef[0]; @@ -35,11 +36,6 @@ public MethodBreakpointInfo(RequestFilter filter) { super(filter); } - @Override - public boolean isLineBreakpoint() { - return true; - } - public void addMethod(MethodRef method) { methods = Arrays.copyOf(methods, methods.length + 1); methods[methods.length - 1] = method; @@ -48,4 +44,19 @@ public void addMethod(MethodRef method) { public MethodRef[] getMethods() { return methods; } + + @Override + public Kind getKind() { + return Kind.INDEFINITE; + } + + @Override + public boolean onMethodEnter(@SuppressWarnings("unused") MethodRef method, @SuppressWarnings("unused") MethodVariable[] variables) { + return true; + } + + @Override + public boolean onMethodExit(@SuppressWarnings("unused") MethodRef method, @SuppressWarnings("unused") Object returnValue) { + return true; + } } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/RequestedJDWPEvents.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/RequestedJDWPEvents.java index 68b2acbaa63f..f0d8b336dd18 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/RequestedJDWPEvents.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/RequestedJDWPEvents.java @@ -111,7 +111,7 @@ public CommandResult registerEvent(Packet packet, Commands callback) { eventListener.addBreakpointRequest(filter.getRequestId(), methodInfo); for (KlassRef klass : filter.getKlassRefPatterns()) { for (MethodRef method : klass.getDeclaredMethodRefs()) { - method.addMethodBreakpointInfo(methodInfo); + method.addMethodHook(methodInfo); methodInfo.addMethod(method); } } @@ -334,7 +334,7 @@ public CommandResult clearRequest(Packet packet) { case METHOD_EXIT: MethodBreakpointInfo methodInfo = (MethodBreakpointInfo) requestFilter.getBreakpointInfo(); for (MethodRef method : methodInfo.getMethods()) { - method.removeMethodBreakpointInfo(requestFilter.getRequestId()); + method.removedMethodHook(requestFilter.getRequestId()); } break; case BREAKPOINT: diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java index b4b874e81d1a..c8a964b38d39 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java @@ -33,20 +33,27 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.debug.Breakpoint; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.espresso.jdwp.api.CallFrame; import com.oracle.truffle.espresso.jdwp.api.FieldBreakpoint; import com.oracle.truffle.espresso.jdwp.api.FieldRef; import com.oracle.truffle.espresso.jdwp.api.Ids; import com.oracle.truffle.espresso.jdwp.api.JDWPContext; import com.oracle.truffle.espresso.jdwp.api.KlassRef; -import com.oracle.truffle.espresso.jdwp.api.MethodBreakpoint; +import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVariable; import com.oracle.truffle.espresso.jdwp.api.TagConstants; import sun.reflect.generics.reflectiveObjects.NotImplementedException; public final class VMEventListenerImpl implements VMEventListener { + public static final InteropLibrary UNCACHED = InteropLibrary.getUncached(); + private final Ids ids; private final JDWPContext context; private final DebuggerController debuggerController; @@ -143,15 +150,67 @@ public boolean onFieldAccess(FieldRef field, Object receiver) { return active; } + @Override + @TruffleBoundary + public boolean onMethodEntry(MethodRef method, Object scope) { + boolean active = false; + // collect variable information from scope + List variables = new ArrayList<>(1); + try { + if (UNCACHED.hasMembers(scope)) { + Object identifiers = UNCACHED.getMembers(scope); + if (UNCACHED.hasArrayElements(identifiers)) { + long size = UNCACHED.getArraySize(identifiers); + for (long i = 0; i < size; i++) { + String identifier = (String) UNCACHED.readArrayElement(identifiers, i); + Object value = UNCACHED.readMember(scope, identifier); + variables.add(new MethodVariable(identifier, value)); + } + } + } + } catch (UnsupportedMessageException | InvalidArrayIndexException | UnknownIdentifierException e) { + // not able to fetch locals, so leave variables list empty + } + + for (MethodHook hook : method.getMethodHooks()) { + // pass on the variables to the method entry hook + if (hook.onMethodEnter(method, variables.toArray(new MethodVariable[variables.size()]))) { + // OK, tell the Debug API to suspend the thread now + debuggerController.prepareMethodBreakpoint(new MethodBreakpointEvent((MethodBreakpointInfo) hook, null)); + debuggerController.suspend(context.asGuestThread(Thread.currentThread())); + active = true; + } + switch (hook.getKind()) { + case ONE_TIME: + method.removedMethodHook(-1); + break; + case INDEFINITE: + // leave the hook active + break; + } + } + return active; + } + @Override @TruffleBoundary public boolean onMethodReturn(MethodRef method, Object returnValue) { boolean active = false; - for (MethodBreakpoint info : method.getMethodBreakpointInfos()) { - // OK, tell the Debug API to suspend the thread now - debuggerController.prepareMethodBreakpoint(new MethodBreakpointEvent((MethodBreakpointInfo) info, returnValue)); - debuggerController.suspend(context.asGuestThread(Thread.currentThread())); - active = true; + for (MethodHook hook : method.getMethodHooks()) { + if (hook.onMethodExit(method, returnValue)) { + // OK, tell the Debug API to suspend the thread now + debuggerController.prepareMethodBreakpoint(new MethodBreakpointEvent((MethodBreakpointInfo) hook, returnValue)); + debuggerController.suspend(context.asGuestThread(Thread.currentThread())); + active = true; + } + switch (hook.getKind()) { + case ONE_TIME: + method.removedMethodHook(-1); + break; + case INDEFINITE: + // leave the hook active + break; + } } return active; } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java new file mode 100644 index 000000000000..c8f11f5f23c3 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +@SuppressWarnings("unused") +public final class EspressoHotSwap { + + private EspressoHotSwap() { + throw new RuntimeException("No instance of EspressoHotSwap can be created"); + } + + private static final HotSwapHandler handler = HotSwapHandler.create(); + + public static void registerPlugin(HotSwapPlugin plugin) { + if (handler != null) { + handler.addPlugin(plugin); + } else { + // TODO - should we log that plugin registration is only available on supported Espresso VM? + } + } + + public static void registerHotSwapAction(Class klass, HotSwapAction action) { + if (handler != null) { + handler.registerHotSwapAction(klass, action); + } + } + + public static void registerClassInitHotSwap(Class klass, boolean onChange) { + if (handler != null) { + handler.registerStaticClassInitHotSwap(klass, onChange); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java new file mode 100644 index 000000000000..1e43bd871e68 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +public interface HotSwapAction { + void onHotSwap(); +} diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java new file mode 100644 index 000000000000..4a4bee7ff740 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +final class HotSwapHandler { + + private static HotSwapHandler theHandler; + + private final Set plugins = Collections.synchronizedSet(new HashSet<>()); + private final Map, Set> hotSwapActions = new HashMap<>(); + private final Map, Boolean> staticInitializerHotSwap = new HashMap<>(); + + private HotSwapHandler() { + } + + static HotSwapHandler create() { + theHandler = new HotSwapHandler(); + // register handler to Espresso if present + return registerHandler(theHandler) ? theHandler : null; + } + + // substituted by Espresso + private static boolean registerHandler(@SuppressWarnings("unused") Object handler) { + return false; + } + + void addPlugin(HotSwapPlugin plugin) { + plugins.add(plugin); + } + + public void registerHotSwapAction(Class klass, HotSwapAction action) { + hotSwapActions.putIfAbsent(klass, new HashSet<>()); + hotSwapActions.get(klass).add(action); + } + + public void registerStaticClassInitHotSwap(Class klass, boolean onChange) { + if (!staticInitializerHotSwap.containsKey(klass)) { + staticInitializerHotSwap.put(klass, onChange); + } else if (!onChange) { + staticInitializerHotSwap.put(klass, false); + } + } + + @SuppressWarnings("unused") + public void postHotSwap(Class[] changedClasses) { + // fire all registered HotSwap actions + for (Class klass : changedClasses) { + Set actions = hotSwapActions.getOrDefault(klass, Collections.emptySet()); + actions.forEach(HotSwapAction::onHotSwap); + } + // fire a generic HotSwap plugin listener + for (HotSwapPlugin plugin : plugins) { + plugin.postHotSwap(); + } + } + + @SuppressWarnings("unused") + public boolean rerunClassInit(Class klass, boolean changed) { + if (staticInitializerHotSwap.containsKey(klass)) { + boolean onlyOnChange = staticInitializerHotSwap.get(klass); + return !onlyOnChange || changed; + } + return false; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java new file mode 100644 index 000000000000..edfd48170d98 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +public interface HotSwapPlugin { + String getName(); + + void postHotSwap(); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin new file mode 100644 index 000000000000..a03c21cb2019 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin @@ -0,0 +1,2 @@ +com.oracle.truffle.espresso.redefinition.plugins.micronaut.MicronautPlugin +com.oracle.truffle.espresso.redefinition.plugins.jdkproxy.JDKProxyRedefinitionPlugin diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java index 20b1c79c0acf..a31ee895f5cb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java @@ -39,6 +39,7 @@ import com.oracle.truffle.espresso.descriptors.Symbol.Type; import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.ClassLoadListener; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; import com.oracle.truffle.espresso.substitutions.Host; @@ -61,6 +62,8 @@ public final class ClassRegistries { // specify it as volatile. private int totalClassLoadersSet = 0; + private ClassLoadListener classLoadListener; + public ClassRegistries(EspressoContext context) { this.context = context; this.bootClassRegistry = new BootClassRegistry(context); @@ -231,14 +234,14 @@ public void checkLoadingConstraint(Symbol type, StaticObject loader1, Stat } } - void recordConstraint(Symbol type, Klass klass, StaticObject loader) { + public void recordConstraint(Symbol type, Klass klass, StaticObject loader) { assert !Types.isArray(type); if (!Types.isPrimitive(type)) { constraints.recordConstraint(type, klass, loader); } } - void removeUnloadedKlassConstraint(Klass klass, Symbol type) { + public void removeUnloadedKlassConstraint(Klass klass, Symbol type) { assert klass.isInstanceClass(); constraints.removeUnloadedKlassConstraint(klass, type); } @@ -298,6 +301,16 @@ int[] aliveLoaders() { return loaders; } + public void registerListener(ClassLoadListener listener) { + this.classLoadListener = listener; + } + + public void onKlassDefined(ObjectKlass klass) { + if (classLoadListener != null) { + classLoadListener.onClassLoad(klass); + } + } + static class RegistryEntry { private final Klass klass; private volatile Set domains = null; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java index 0e6f24c7fa4c..fbac5a3f81f6 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java @@ -40,6 +40,7 @@ import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.perf.DebugCloseable; import com.oracle.truffle.espresso.perf.DebugTimer; +import com.oracle.truffle.espresso.redefinition.DefineKlassListener; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; import com.oracle.truffle.espresso.runtime.StaticObject; @@ -347,6 +348,7 @@ private ObjectKlass createAndPutKlass(Meta meta, ParserKlass parserKlass, Symbol EspressoError.guarantee(previous == null, "Class " + type + " is already defined"); getRegistries().recordConstraint(type, klass, getClassLoader()); + getRegistries().onKlassDefined(klass); if (defineKlassListener != null) { defineKlassListener.onKlassDefined(klass); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ConstantPoolPatcher.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ConstantPoolPatcher.java index 837ced3f71f5..5d9d1c09751a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ConstantPoolPatcher.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ConstantPoolPatcher.java @@ -26,6 +26,7 @@ import com.oracle.truffle.espresso.classfile.ConstantPool; import com.oracle.truffle.espresso.descriptors.ByteSequence; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.redefinition.InnerClassRedefiner; import com.oracle.truffle.espresso.runtime.EspressoContext; import java.lang.instrument.IllegalClassFormatException; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java index 4e5d89d23f62..5270bde17c6a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java @@ -131,7 +131,7 @@ Symbol getName() { return parserKlass.getName(); } - ParserKlass getParserKlass() { + public ParserKlass getParserKlass() { return parserKlass; } 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 e8735e127688..1bdafd6bb0e5 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 @@ -80,7 +80,7 @@ import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.LineNumberTableRef; import com.oracle.truffle.espresso.jdwp.api.LocalVariableTableRef; -import com.oracle.truffle.espresso.jdwp.api.MethodBreakpoint; +import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; import com.oracle.truffle.espresso.jni.Mangle; import com.oracle.truffle.espresso.meta.EspressoError; @@ -92,6 +92,7 @@ import com.oracle.truffle.espresso.nodes.EspressoRootNode; import com.oracle.truffle.espresso.nodes.NativeMethodNode; import com.oracle.truffle.espresso.nodes.methodhandle.MethodHandleIntrinsicNode; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.runtime.Attribute; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics; @@ -996,49 +997,49 @@ public String getGenericSignatureAsString() { return genericSignature; } - private final Field.StableBoolean hasActiveBreakpoints = new Field.StableBoolean(false); + private final Field.StableBoolean hasActiveHook = new Field.StableBoolean(false); - private MethodBreakpoint[] infos = new MethodBreakpoint[0]; + private MethodHook[] hooks = new MethodHook[0]; - public boolean hasActiveBreakpoint() { - return hasActiveBreakpoints.get(); + public boolean hasActiveHook() { + return hasActiveHook.get(); } - public MethodBreakpoint[] getMethodBreakpointInfos() { - return infos; + public MethodHook[] getMethodHooks() { + return hooks; } - public void addMethodBreakpointInfo(MethodBreakpoint info) { - hasActiveBreakpoints.set(true); - if (infos.length == 0) { - infos = new MethodBreakpoint[]{info}; + public void addMethodHook(MethodHook info) { + hasActiveHook.set(true); + if (hooks.length == 0) { + hooks = new MethodHook[]{info}; return; } - infos = Arrays.copyOf(infos, infos.length + 1); - infos[infos.length - 1] = info; + hooks = Arrays.copyOf(hooks, hooks.length + 1); + hooks[hooks.length - 1] = info; } - public void removeMethodBreakpointInfo(int requestId) { + public void removeActiveHook(int requestId) { // shrink the array to avoid null values - if (infos.length == 0) { - throw new RuntimeException("Method: " + getNameAsString() + " should contain method breakpoint info"); - } else if (infos.length == 1) { - infos = new MethodBreakpoint[0]; - hasActiveBreakpoints.set(false); + if (hooks.length == 0) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); + } else if (hooks.length == 1) { + hooks = new MethodHook[0]; + hasActiveHook.set(false); } else { int removeIndex = -1; - for (int i = 0; i < infos.length; i++) { - if (infos[i].getRequestId() == requestId) { + for (int i = 0; i < hooks.length; i++) { + if (hooks[i].getRequestId() == requestId) { removeIndex = i; break; } } - MethodBreakpoint[] temp = new MethodBreakpoint[infos.length - 1]; + MethodHook[] temp = new MethodHook[hooks.length - 1]; for (int i = 0; i < temp.length; i++) { - temp[i] = i < removeIndex ? infos[i] : infos[i + 1]; + temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; } - infos = temp; + hooks = temp; } } @@ -1372,23 +1373,23 @@ public int getLastLine() { } @Override - public MethodBreakpoint[] getMethodBreakpointInfos() { - return getMethod().getMethodBreakpointInfos(); + public MethodHook[] getMethodHooks() { + return getMethod().getMethodHooks(); } @Override - public void addMethodBreakpointInfo(MethodBreakpoint info) { - getMethod().addMethodBreakpointInfo(info); + public void addMethodHook(MethodHook info) { + getMethod().addMethodHook(info); } @Override - public void removeMethodBreakpointInfo(int requestId) { - getMethod().removeMethodBreakpointInfo(requestId); + public void removedMethodHook(int requestId) { + getMethod().removeActiveHook(requestId); } @Override - public boolean hasActiveBreakpoint() { - return getMethod().hasActiveBreakpoint(); + public boolean hasActiveHook() { + return getMethod().hasActiveHook(); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index 9c35747c5a05..e6d1f6944f0a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -68,6 +68,9 @@ import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.ChangePacket; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.DetectedChange; import com.oracle.truffle.espresso.runtime.Attribute; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; @@ -1200,6 +1203,11 @@ public void redefineClass(ChangePacket packet, List refreshSubClass oldVersion.assumption.invalidate(); } + // used by some plugins during klass redefitnion + public void reRunClinit() { + getClassInitializer().getCallTarget().call(); + } + private static void checkCopyMethods(Method method, Method[][] table, Method.SharedRedefinitionContent content, Ids ids) { for (Method[] methods : table) { checkCopyMethods(method, methods, content, ids); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserKlass.java index cd905632bdb6..07015a0c8d7d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserKlass.java @@ -114,15 +114,15 @@ public ConstantPool getConstantPool() { return pool; } - ParserMethod[] getMethods() { + public ParserMethod[] getMethods() { return methods; } - ParserField[] getFields() { + public ParserField[] getFields() { return fields; } - Attribute getAttribute(Symbol attributeName) { + public Attribute getAttribute(Symbol attributeName) { for (Attribute attribute : attributes) { if (attributeName.equals(attribute.getName())) { return attribute; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 72fd5b10491b..6ec6f3a9f3de 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -656,7 +656,7 @@ Object executeBody(VirtualFrame frame) { setBCI(frame, curBCI); if (instrument != null) { - instrument.notifyEntry(frame); + instrument.notifyEntry(frame, this); } onStart(primitives, refs); @@ -2555,15 +2555,16 @@ void notifyStatement(VirtualFrame frame, int statementIndex, int nextStatementIn enterAt(frame, nextStatementIndex); } - public void notifyEntry(@SuppressWarnings("unused") VirtualFrame frame) { - // TODO(Gregersen) - method entry breakpoints are currently implemented by submitting - // first line breakpoints within each method. This works insofar the method has a valid - // line table. For classes compiled without debug information we could use this hook - // instead. + public void notifyEntry(@SuppressWarnings("unused") VirtualFrame frame, EspressoInstrumentableNode instrumentableNode) { + if (method.hasActiveHook()) { + if (context.getJDWPListener().onMethodEntry(method, instrumentableNode.getScope(frame, true))) { + enterAt(frame, 0); + } + } } public void notifyReturn(VirtualFrame frame, int statementIndex, Object returnValue) { - if (method.hasActiveBreakpoint()) { + if (method.hasActiveHook()) { if (context.getJDWPListener().onMethodReturn(method, returnValue)) { enterAt(frame, statementIndex); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java index e31180e2cd44..25c080c57d3f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java @@ -34,7 +34,7 @@ import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.meta.EspressoError; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/methodhandle/MHLinkToNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/methodhandle/MHLinkToNode.java index 84e123642151..a8b2e3afc941 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/methodhandle/MHLinkToNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/methodhandle/MHLinkToNode.java @@ -35,7 +35,7 @@ import com.oracle.truffle.espresso.descriptors.Signatures; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Type; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Method; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java index fb7f0eab5043..a6801a9ee016 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java @@ -29,7 +29,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java index 01bdcff92c9a..1e264fe811d0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java @@ -27,7 +27,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.nodes.BytecodeNode; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java index c159f11e8ba2..b95fc83d6350 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java @@ -28,7 +28,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; import com.oracle.truffle.espresso.descriptors.Symbol.Name; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.nodes.BytecodeNode; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java index 192312e393ed..057dbe1e589b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java @@ -29,7 +29,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ChangePacket.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ChangePacket.java similarity index 91% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ChangePacket.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ChangePacket.java index 849d6f965d80..5087b34525d1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ChangePacket.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ChangePacket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2021, 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 @@ -20,7 +20,9 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; + +import com.oracle.truffle.espresso.impl.ParserKlass; public final class ChangePacket { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassInfo.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassInfo.java similarity index 95% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassInfo.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassInfo.java index e3d6544b2fc0..5d3c32ca172b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassInfo.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassInfo.java @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import com.oracle.truffle.espresso.classfile.ClassfileParser; import com.oracle.truffle.espresso.classfile.ClassfileStream; @@ -28,6 +28,13 @@ import com.oracle.truffle.espresso.classfile.attributes.EnclosingMethodAttribute; import com.oracle.truffle.espresso.classfile.constantpool.NameAndTypeConstant; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.impl.ParserField; +import com.oracle.truffle.espresso.impl.ParserKlass; +import com.oracle.truffle.espresso.impl.ParserMethod; import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java new file mode 100644 index 000000000000..c22bbbcff6d9 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java @@ -0,0 +1,7 @@ +package com.oracle.truffle.espresso.redefinition; + +import com.oracle.truffle.espresso.impl.ObjectKlass; + +public interface ClassLoadListener { + void onClassLoad(ObjectKlass klass); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java similarity index 92% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRedefinition.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index f9f9c6d91be1..de4ec17d3fb1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import java.util.ArrayList; import java.util.Arrays; @@ -46,10 +46,20 @@ import com.oracle.truffle.espresso.classfile.attributes.LocalVariableTable; import com.oracle.truffle.espresso.classfile.constantpool.PoolConstant; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ClassRegistry; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.impl.ParserField; +import com.oracle.truffle.espresso.impl.ParserKlass; +import com.oracle.truffle.espresso.impl.ParserMethod; import com.oracle.truffle.espresso.jdwp.api.ErrorCodes; import com.oracle.truffle.espresso.jdwp.api.Ids; import com.oracle.truffle.espresso.jdwp.api.KlassRef; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefineListener; import com.oracle.truffle.espresso.runtime.Attribute; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; @@ -65,6 +75,14 @@ public final class ClassRedefinition { private static volatile boolean locked = false; private static Thread redefineThread = null; + private final EspressoContext context; + private final Ids ids; + private final RedefineListener redefineListener; + + public void addExtraReloadClasses(List redefineInfos, List additional) { + redefineListener.addExtraReloadClasses(redefineInfos, additional); + } + private enum RedefinitionSupport { METHOD_BODY, ADD_METHOD, @@ -86,6 +104,12 @@ enum ClassChange { INVALID; } + public ClassRedefinition(EspressoContext context, Ids ids, RedefineListener listener) { + this.context = context; + this.ids = ids; + this.redefineListener = listener; + } + public static void lock() { synchronized (redefineLock) { check(); @@ -126,6 +150,10 @@ public static void end() { } } + public void runPostRedefintionListeners(ObjectKlass[] changedKlasses) { + redefineListener.postRedefition(changedKlasses); + } + private static class RedefineAssumption { private final Assumption assumption = Truffle.getRuntime().createAssumption(); } @@ -153,7 +181,7 @@ public static void check() { } } - public static List detectClassChanges(HotSwapClassInfo[] classInfos, EspressoContext context) { + public List detectClassChanges(HotSwapClassInfo[] classInfos) throws RedefintionNotSupportedException { List result = new ArrayList<>(classInfos.length); for (HotSwapClassInfo hotSwapInfo : classInfos) { KlassRef klass = hotSwapInfo.getKlass(); @@ -185,40 +213,46 @@ public static List detectClassChanges(HotSwapClassInfo[] classInfo return result; } - public static int redefineClass(ChangePacket packet, Ids ids, EspressoContext context, List refreshSubClasses) { + public int redefineClass(ChangePacket packet, List refreshSubClasses) { try { switch (packet.classChange) { case METHOD_BODY_CHANGE: case CONSTANT_POOL_CHANGE: case CLASS_NAME_CHANGED: - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; case ADD_METHOD: if (isAddMethodSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.ADD_METHOD_NOT_IMPLEMENTED; } case REMOVE_METHOD: if (isRemoveMethodSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.DELETE_METHOD_NOT_IMPLEMENTED; } case SCHEMA_CHANGE: if (isArbitraryChangesSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.SCHEMA_CHANGE_NOT_IMPLEMENTED; } case CLASS_MODIFIERS_CHANGE: if (isArbitraryChangesSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED; } case HIERARCHY_CHANGE: if (isArbitraryChangesSupported()) { - return doRedefineClass(packet, ids, context, refreshSubClasses); + doRedefineClass(packet, refreshSubClasses); + return 0; } else { return ErrorCodes.HIERARCHY_CHANGE_NOT_IMPLEMENTED; } @@ -263,7 +297,7 @@ private static boolean isRemoveMethodSupported() { // detect all types of class changes, but return early when a change that require arbitrary // changes - private static ClassChange detectClassChanges(ParserKlass newParserKlass, ObjectKlass oldKlass, DetectedChange collectedChanges, ParserKlass finalParserKlass) { + private static ClassChange detectClassChanges(ParserKlass newParserKlass, ObjectKlass oldKlass, DetectedChange collectedChanges, ParserKlass finalParserKlass) throws RedefintionNotSupportedException { ClassChange result = ClassChange.NO_CHANGE; ParserKlass oldParserKlass = oldKlass.getLinkedKlass().getParserKlass(); boolean isPatched = finalParserKlass != null; @@ -605,7 +639,7 @@ private static boolean isUnchangedField(Field oldField, ParserField newField) { return false; } - private static int doRedefineClass(ChangePacket packet, Ids ids, EspressoContext context, List refreshSubClasses) { + private void doRedefineClass(ChangePacket packet, List refreshSubClasses) { ObjectKlass oldKlass = packet.info.getKlass(); ClassRegistry classRegistry = context.getRegistries().getClassRegistry(packet.info.getClassLoader()); if (packet.info.isRenamed()) { @@ -629,7 +663,9 @@ private static int doRedefineClass(ChangePacket packet, Ids ids, Espress context.getRegistries().recordConstraint(type, oldKlass, oldKlass.getDefiningClassLoader()); } oldKlass.redefineClass(packet, refreshSubClasses, ids); - return 0; + if (redefineListener.rerunClinit(oldKlass, packet.detectedChange.clinitChanged())) { + oldKlass.reRunClinit(); + } } @TruffleBoundary diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DefineKlassListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DefineKlassListener.java similarity index 91% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DefineKlassListener.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DefineKlassListener.java index 1f91325ba4f5..68c97609a2ff 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DefineKlassListener.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DefineKlassListener.java @@ -20,7 +20,9 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; + +import com.oracle.truffle.espresso.impl.ObjectKlass; public interface DefineKlassListener { void onKlassDefined(ObjectKlass klass); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DetectedChange.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DetectedChange.java similarity index 76% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DetectedChange.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DetectedChange.java index 905333627a28..f52f0176f8d4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/DetectedChange.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/DetectedChange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, 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 @@ -20,7 +20,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; + +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ParserMethod; import java.util.ArrayList; import java.util.Collections; @@ -30,25 +35,29 @@ import java.util.Map; import java.util.Set; -final class DetectedChange { +public final class DetectedChange { private final Map changedMethodBodies = new HashMap<>(); private final List addedMethods = new ArrayList<>(); private final Set removedMethods = new HashSet<>(); private final List outerFields = new ArrayList<>(); + private boolean clinitChanged; void addMethodBodyChange(Method oldMethod, ParserMethod newMethod) { changedMethodBodies.put(oldMethod, newMethod); + if (oldMethod.getName() == Symbol.Name._clinit_) { + clinitChanged = true; + } } - Map getChangedMethodBodies() { + public Map getChangedMethodBodies() { return Collections.unmodifiableMap(changedMethodBodies); } - List getAddedMethods() { + public List getAddedMethods() { return Collections.unmodifiableList(addedMethods); } - Set getRemovedMethods() { + public Set getRemovedMethods() { return Collections.unmodifiableSet(removedMethods); } @@ -75,4 +84,8 @@ public void addOuterField(Field oldField) { public List getOuterFields() { return Collections.unmodifiableList(outerFields); } + + public boolean clinitChanged() { + return clinitChanged; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/HotSwapClassInfo.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/HotSwapClassInfo.java similarity index 98% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/HotSwapClassInfo.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/HotSwapClassInfo.java index 82378d009a61..a3330169b12d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/HotSwapClassInfo.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/HotSwapClassInfo.java @@ -20,9 +20,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.runtime.StaticObject; import java.lang.ref.WeakReference; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ImmutableClassInfo.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ImmutableClassInfo.java similarity index 97% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ImmutableClassInfo.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ImmutableClassInfo.java index f393442b6c44..4d83f05aa95a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ImmutableClassInfo.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ImmutableClassInfo.java @@ -20,9 +20,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.runtime.StaticObject; import java.lang.ref.WeakReference; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/InnerClassRedefiner.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java similarity index 91% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/InnerClassRedefiner.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java index c4b4b6a48126..db2bb2e0f7ca 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/InnerClassRedefiner.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java @@ -20,10 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; import com.oracle.truffle.espresso.classfile.ClassfileParser; import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ClassRegistry; +import com.oracle.truffle.espresso.impl.ConstantPoolPatcher; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.jdwp.api.ErrorCodes; import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; @@ -35,7 +39,6 @@ import java.lang.instrument.IllegalClassFormatException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -46,7 +49,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public final class InnerClassRedefiner implements DefineKlassListener { +public final class InnerClassRedefiner { public static final Pattern ANON_INNER_CLASS_PATTERN = Pattern.compile(".*\\$\\d+.*"); public static final int METHOD_FINGERPRINT_EQUALS = 8; @@ -72,11 +75,11 @@ public InnerClassRedefiner(EspressoContext context) { this.context = context; } - public HotSwapClassInfo[] matchAnonymousInnerClasses(RedefineInfo[] redefineInfos, List removedInnerClasses) { + public HotSwapClassInfo[] matchAnonymousInnerClasses(List redefineInfos, List removedInnerClasses) throws RedefintionNotSupportedException { hotswapState.clear(); - ArrayList unhandled = new ArrayList<>(redefineInfos.length); - Collections.addAll(unhandled, redefineInfos); - Map, HotSwapClassInfo> handled = new HashMap<>(redefineInfos.length); + ArrayList unhandled = new ArrayList<>(redefineInfos); + + Map, HotSwapClassInfo> handled = new HashMap<>(redefineInfos.size()); // build inner/outer relationship from top-level to leaf class in order // each round below handles classes where the outer class was previously // handled @@ -149,7 +152,7 @@ private static void collectAllHotswapClasses(Collection infos, } } - private void fetchMissingInnerClasses(HotSwapClassInfo hotswapInfo) { + private void fetchMissingInnerClasses(HotSwapClassInfo hotswapInfo) throws RedefintionNotSupportedException { StaticObject definingLoader = hotswapInfo.getClassLoader(); ArrayList> innerNames = new ArrayList<>(1); @@ -215,7 +218,7 @@ private Symbol getOuterClassName(Symbol innerName) { return context.getNames().getOrCreate(strName.substring(0, strName.lastIndexOf('$'))); } - private void matchClassInfo(HotSwapClassInfo hotSwapInfo, List removedInnerClasses, Map, Symbol>> renamingRules) { + private void matchClassInfo(HotSwapClassInfo hotSwapInfo, List removedInnerClasses, Map, Symbol>> renamingRules) throws RedefintionNotSupportedException { Klass klass = hotSwapInfo.getKlass(); // try to fetch all direct inner classes // based on the constant pool in the class bytes @@ -326,7 +329,12 @@ Set findLoadedInnerClasses(Klass klass) { // register a listener on the registry to fill in // future loaded anonymous inner classes ClassRegistry classRegistry = context.getRegistries().getClassRegistry(klass.getDefiningClassLoader()); - classRegistry.registerOnLoadListener(this); + classRegistry.registerOnLoadListener(new DefineKlassListener() { + @Override + public void onKlassDefined(ObjectKlass objectKlass) { + InnerClassRedefiner.this.onKlassDefined(objectKlass); + } + }); // do a one-time look up of all currently loaded // classes for this loader and fill in the map @@ -334,15 +342,18 @@ Set findLoadedInnerClasses(Klass klass) { for (Klass loadedKlass : loadedKlasses) { if (loadedKlass instanceof ObjectKlass) { ObjectKlass objectKlass = (ObjectKlass) loadedKlass; - Symbol klassName = loadedKlass.getName(); - if (klassName.toString().contains("$")) { - Symbol outerType = context.getTypes().fromName(getOuterClassName(klassName)); - Set innerKlasses = classLoaderMap.get(outerType); - if (innerKlasses == null) { - innerKlasses = new HashSet<>(1); - classLoaderMap.put(outerType, innerKlasses); + Matcher matcher = ANON_INNER_CLASS_PATTERN.matcher(klass.getNameAsString()); + if (matcher.matches()) { + Symbol outerClassName = getOuterClassName(loadedKlass.getName()); + if (outerClassName != null && outerClassName.length() > 0) { + Symbol outerType = context.getTypes().fromName(outerClassName); + Set innerKlasses = classLoaderMap.get(outerType); + if (innerKlasses == null) { + innerKlasses = new HashSet<>(1); + classLoaderMap.put(outerType, innerKlasses); + } + innerKlasses.add(objectKlass); } - innerKlasses.add(objectKlass); } } } @@ -353,8 +364,7 @@ Set findLoadedInnerClasses(Klass klass) { return innerClasses != null ? innerClasses : new HashSet<>(0); } - @Override - public void onKlassDefined(ObjectKlass klass) { + private void onKlassDefined(ObjectKlass klass) { Matcher matcher = ANON_INNER_CLASS_PATTERN.matcher(klass.getNameAsString()); if (matcher.matches()) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/RedefintionNotSupportedException.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/RedefintionNotSupportedException.java similarity index 83% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/RedefintionNotSupportedException.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/RedefintionNotSupportedException.java index b5f8e409baf5..c2d3cbecde85 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/RedefintionNotSupportedException.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/RedefintionNotSupportedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2021, 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 @@ -20,14 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.redefinition; -public class RedefintionNotSupportedException extends RuntimeException { +public class RedefintionNotSupportedException extends Exception { private static final long serialVersionUID = -5767957395371919542L; private final int errorCode; - RedefintionNotSupportedException(int errorCode) { + public RedefintionNotSupportedException(int errorCode) { this.errorCode = errorCode; } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodBreakpoint.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/ClassLoadAction.java similarity index 80% rename from espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodBreakpoint.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/ClassLoadAction.java index 12ce73cc3667..e23a4b73acdf 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodBreakpoint.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/ClassLoadAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2021, 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 @@ -20,8 +20,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.jdwp.api; +package com.oracle.truffle.espresso.redefinition.plugins.api; -public interface MethodBreakpoint { - int getRequestId(); +import com.oracle.truffle.espresso.jdwp.api.KlassRef; + +public interface ClassLoadAction { + void fire(KlassRef klass); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java new file mode 100644 index 000000000000..02dc68091ffd --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import java.util.Collection; +import java.util.List; + +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.RedefineInfo; +import com.oracle.truffle.espresso.redefinition.plugins.impl.CachedRedefineObject; +import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefinitionPluginHandler; +import com.oracle.truffle.espresso.redefinition.plugins.impl.UncachedRedefineObject; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; +import com.oracle.truffle.espresso.substitutions.Host; + +public abstract class InternalRedefinitionPlugin { + + private static final String constructorName = ""; + + private EspressoContext context; + private RedefinitionPluginHandler redefinitionPluginHandler; + + public EspressoContext getContext() { + return context; + } + + public void activate(EspressoContext espressoContext, RedefinitionPluginHandler handler) { + this.context = espressoContext; + this.redefinitionPluginHandler = handler; + } + + public abstract String getName(); + + public abstract TriggerClass[] getTriggerClasses(); + + public boolean reRunClinit(@SuppressWarnings("unused") KlassRef klass, @SuppressWarnings("unused") boolean changed) { + return false; + } + + public void fillExtraReloadClasses(@SuppressWarnings("unused") List redefineInfos, @SuppressWarnings("unused") List additional) { + // default does nothing + } + + public void postClassRedefinition(@SuppressWarnings("unused") KlassRef[] changedKlasses) { + // default does nothing + } + + public static RedefineObject createUncached(Object instance) { + return new UncachedRedefineObject((StaticObject) instance); + } + + public static RedefineObject createCached(Object instance) { + return new CachedRedefineObject((StaticObject) instance); + } + + public static RedefineObject createCached(KlassRef klass) { + return new CachedRedefineObject(klass); + } + + protected KlassRef getreflectedKlassType(Object classObject) { + return context.getJdwpContext().getReflectedType(classObject); + } + + protected void registerClassLoadAction(String className, ClassLoadAction action) { + redefinitionPluginHandler.registerClassLoadAction(className, action); + } + + protected void hookMethodExit(KlassRef klass, MethodLocator hookSpec, MethodHook.Kind kind, MethodExitHook onExitHook) { + for (MethodRef method : klass.getDeclaredMethodRefs()) { + if (method.getNameAsString().equals(hookSpec.getName()) && method.getSignatureAsString().equals(hookSpec.getSignature())) { + method.addMethodHook(new RedefintionHook(onExitHook, kind)); + break; + } + } + } + + protected void hookMethodEntry(KlassRef klass, MethodLocator hookSpec, MethodHook.Kind kind, MethodEntryHook onEntryHook) { + for (MethodRef method : klass.getDeclaredMethodRefs()) { + if (method.getNameAsString().equals(hookSpec.getName()) && method.getSignatureAsString().equals(hookSpec.getSignature())) { + method.addMethodHook(new RedefintionHook(onEntryHook, kind)); + break; + } + } + } + + protected void hookConstructor(KlassRef klass, MethodHook.Kind kind, MethodEntryHook onEntryHook) { + for (MethodRef method : klass.getDeclaredMethodRefs()) { + if (method.getNameAsString().equals(constructorName)) { + method.addMethodHook(new RedefintionHook(onEntryHook, kind)); + } + } + } + + protected void clearCollection(@Host(Collection.class) RedefineObject object, String fieldName) throws NoSuchFieldException, NoSuchMethodException { + RedefineObject collectionField = object.getInstanceField(fieldName); + if (collectionField != null) { + collectionField.invokeRaw("clear"); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java new file mode 100644 index 000000000000..64051524f18c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVariable; + +public interface MethodEntryHook { + void onMethodEnter(MethodRef method, MethodVariable[] variables); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java new file mode 100644 index 000000000000..f9fa5eb5eec5 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.MethodRef; + +public interface MethodExitHook { + void onMethodExit(MethodRef method, Object returnValue); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java new file mode 100644 index 000000000000..7b2137a35dcd --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +public final class MethodLocator { + private final String name; + private final String signature; + + public MethodLocator(String name, String signature) { + this.name = name; + this.signature = signature; + } + + public String getName() { + return name; + } + + public String getSignature() { + return signature; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java new file mode 100644 index 000000000000..60657c5cc659 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.KlassRef; + +public interface RedefineObject { + boolean notNull(); + + KlassRef getKlass(); + + RedefineObject fromType(String className); + + RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException; + + RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException; + + RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java new file mode 100644 index 000000000000..f084ffc291c1 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java @@ -0,0 +1,7 @@ +package com.oracle.truffle.espresso.redefinition.plugins.api; + +import java.util.Set; + +public interface RedefinitionAction { + Set onChange(); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java new file mode 100644 index 000000000000..87a5c622113b --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.MethodHook; +import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVariable; + +public final class RedefintionHook implements MethodHook { + + private final MethodExitHook onExitHook; + private final MethodEntryHook onEntryHook; + private final Kind kind; + + public RedefintionHook(MethodEntryHook onEntryHook, Kind kind) { + this.onExitHook = null; + this.onEntryHook = onEntryHook; + this.kind = kind; + } + + public RedefintionHook(MethodExitHook onExitHook, Kind kind) { + this.onExitHook = onExitHook; + this.onEntryHook = null; + this.kind = kind; + } + + @Override + public Kind getKind() { + return kind; + } + + @Override + public boolean onMethodEnter(MethodRef method, MethodVariable[] variables) { + if (onEntryHook != null) { + onEntryHook.onMethodEnter(method, variables); + } + return false; + } + + @Override + public boolean onMethodExit(MethodRef method, Object returnValue) { + if (onExitHook != null) { + onExitHook.onMethodExit(method, returnValue); + } + return false; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java new file mode 100644 index 000000000000..748ee0b84a0c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.KlassRef; + +public final class TriggerClass { + + private String className; + private final InternalRedefinitionPlugin plugin; + private final TriggerClassHook hook; + + public TriggerClass(String className, InternalRedefinitionPlugin plugin, TriggerClassHook hook) { + this.className = className; + this.plugin = plugin; + this.hook = hook; + } + + public String getClassName() { + return className; + } + + public InternalRedefinitionPlugin getPlugin() { + return plugin; + } + + public void fire(KlassRef klass) { + hook.fire(klass); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java new file mode 100644 index 000000000000..a945fb986193 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; + +import com.oracle.truffle.espresso.jdwp.api.KlassRef; + +public interface TriggerClassHook { + void fire(KlassRef klass); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java new file mode 100644 index 000000000000..de4b5069f218 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import java.util.HashMap; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.jdwp.api.KlassRef; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; + +public final class CachedRedefineObject extends RedefineObjectImpl { + + protected final EspressoContext context; + private final HashMap methodsCache = new HashMap<>(1); + private final HashMap fieldsCache = new HashMap<>(1); + + public CachedRedefineObject(StaticObject object) { + super(object); + this.context = object.getKlass().getContext(); + } + + public CachedRedefineObject(KlassRef klass) { + super(klass); + this.context = ((Klass) klass).getContext(); + } + + @Override + @TruffleBoundary + public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException { + StringBuilder stringBuffer = new StringBuilder(name); + for (RedefineObject arg : args) { + stringBuffer.append(((RedefineObjectImpl) arg).instance.get().getKlass().getNameAsString()); + } + String mapKey = stringBuffer.toString(); + Method method = methodsCache.get(mapKey); + if (method == null) { + method = lookupMethod(name, args); + if (method != null) { + methodsCache.put(mapKey, method); + } + } + if (method != null) { + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException("cannot invoke method on garbage collection instance"); + } + RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; + for (int i = 0; i < args.length; i++) { + internalArgs[i] = (RedefineObjectImpl) args[i]; + } + return InternalRedefinitionPlugin.createUncached(method.invokeDirect(theInstance, rawObjects(internalArgs))); + } + throw new NoSuchMethodException(); + } + + @Override + public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException, IllegalStateException { + // fetch the known declaring class of the method + StaticObject theInstance = instance.get(); + if (instance == null) { + throw new IllegalStateException(); + } + Symbol type = context.getTypes().fromClassGetName(className); + Klass klassRef = context.getRegistries().findLoadedClass(type, klass.getDefiningClassLoader()); + if (klassRef != null) { + Method method = lookupMethod(klassRef, methodName, args); + if (method != null) { + RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; + for (int i = 0; i < args.length; i++) { + internalArgs[i] = (RedefineObjectImpl) args[i]; + } + return InternalRedefinitionPlugin.createUncached(method.invokeDirect(theInstance, rawObjects(internalArgs))); + } + } + throw new NoSuchMethodException(); + } + + @Override + public RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException { + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException("cannot get field on garbage collection instance"); + } + Field field = fieldsCache.get(fieldName); + if (field == null) { + Klass klassRef = klass; + while (klassRef != null) { + for (Field declaredField : klassRef.getDeclaredFields()) { + if (declaredField.getNameAsString().equals(fieldName)) { + field = declaredField; + fieldsCache.put(fieldName, field); + break; + } + } + klassRef = klassRef.getSuperKlass(); + } + if (field == null) { + throw new NoSuchFieldException(); + } + } + return InternalRedefinitionPlugin.createUncached(field.get(theInstance)); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java new file mode 100644 index 000000000000..16158bec18fa --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import com.oracle.truffle.api.interop.ArityException; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.runtime.StaticObject; + +final class ExternalPluginHandler { + + private static InteropLibrary INTEROP; + private static final String RERUN_CLINIT = "rerunClassInit"; + private static final String POST_HOTSWAP = "postHotSwap"; + + private final StaticObject guestHandler; + + private ExternalPluginHandler(StaticObject handler) { + this.guestHandler = handler; + } + + public static ExternalPluginHandler create(StaticObject guestHandler) throws IllegalArgumentException { + INTEROP = InteropLibrary.getFactory().create(guestHandler); + + boolean invocable = INTEROP.isMemberInvocable(guestHandler, RERUN_CLINIT) && + INTEROP.isMemberInvocable(guestHandler, POST_HOTSWAP); + + if (!invocable) { + throw new IllegalArgumentException("guest handler does not implement expected API"); + } + return new ExternalPluginHandler(guestHandler); + } + + public boolean rerunClassInit(Klass klass, boolean changed) { + try { + return (boolean) INTEROP.invokeMember(guestHandler, RERUN_CLINIT, klass.mirror(), changed); + } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException | ArityException e) { + // TODO - log failure to invoke clinit rerun method on external plugin handler + } + return false; + } + + public void postHotSwap(Klass[] changedKlasses) { + try { + StaticObject[] guestClasses = new StaticObject[changedKlasses.length]; + for (int i = 0; i < guestClasses.length; i++) { + guestClasses[i] = changedKlasses[i].mirror(); + } + StaticObject array = StaticObject.createArray(changedKlasses[0].getMeta().java_lang_Class_array, guestClasses); + INTEROP.invokeMember(guestHandler, POST_HOTSWAP, array); + } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException | ArityException e) { + // TODO - log failure to invoke clinit rerun method on external plugin handler + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java new file mode 100644 index 000000000000..74f533381718 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import java.util.List; + +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; + +public interface RedefineListener { + boolean rerunClinit(ObjectKlass klass, boolean changed); + + void postRedefition(ObjectKlass[] changedKlasses); + + void addExtraReloadClasses(List redefineInfos, List additional); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java new file mode 100644 index 000000000000..66de8d0f2491 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.jdwp.api.KlassRef; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; + +import java.lang.ref.WeakReference; + +public abstract class RedefineObjectImpl implements RedefineObject { + + protected final WeakReference instance; + protected final Klass klass; + + protected RedefineObjectImpl(StaticObject object) { + this.instance = new WeakReference<>(object); + this.klass = object.getKlass(); + } + + public RedefineObjectImpl(KlassRef klass) { + this.instance = new WeakReference<>(StaticObject.NULL); + this.klass = (Klass) klass; + } + + @Override + public Klass getKlass() { + return klass; + } + + @Override + public abstract RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException; + + @SuppressWarnings("unused") + public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException { + throw new NoSuchMethodException(className + "." + methodName); + } + + @Override + public abstract RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; + + protected Method lookupMethod(String name, RedefineObject[] args) throws NoSuchMethodException { + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException("cannot invoke method on garbage collection instance"); + } + + Klass currentKlass = klass; + + while (currentKlass != null) { + Method method = lookupMethod(currentKlass, name, args); + if (method != null) { + return method; + } + ObjectKlass[] interfaces = currentKlass.getInterfaces(); + for (ObjectKlass itf : interfaces) { + method = lookupMethod(itf, name, args); + if (method != null && !method.isAbstract()) { + return method; + } + } + currentKlass = currentKlass.getSuperKlass(); + } + return null; + } + + protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args) throws NoSuchMethodException { + for (Method declaredMethod : klassRef.getDeclaredMethods()) { + if (declaredMethod.getNameAsString().equals(name)) { + // match arguments + boolean match = true; + if (declaredMethod.getParameterCount() == args.length) { + Klass[] parameters = declaredMethod.resolveParameterKlasses(); + for (int i = 0; i < args.length; i++) { + if (!parameters[i].isAssignableFrom((Klass) args[i].getKlass())) { + match = false; + break; + } + } + } else if (declaredMethod.isVarargs()) { + // TODO - not implemented yet + throw new NoSuchMethodException("varargs lookup not implemented"); + } + if (match) { + return declaredMethod; + } + } + } + return null; + } + + protected Object[] rawObjects(RedefineObjectImpl[] args) { + Object[] result = new Object[args.length]; + for (int i = 0; i < args.length; i++) { + result[i] = args[i].instance; + } + return result; + } + + public boolean notNull() { + return instance != null && instance.get() != StaticObject.NULL; + } + + public RedefineObject fromType(String className) { + EspressoContext context = instance.get().getKlass().getContext(); + Symbol type = context.getTypes().fromClassGetName(className); + Klass loadedClass = context.getRegistries().findLoadedClass(type, instance.get().getKlass().getDefiningClassLoader()); + if (loadedClass != null) { + return new CachedRedefineObject(loadedClass.mirror()); + } + return null; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java new file mode 100644 index 000000000000..8f271442793e --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; +import com.oracle.truffle.espresso.redefinition.ClassLoadListener; +import com.oracle.truffle.espresso.redefinition.plugins.api.ClassLoadAction; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; + +public final class RedefinitionPluginHandler implements RedefineListener, ClassLoadListener { + + private final EspressoContext context; + private final Set internalPlugins = Collections.synchronizedSet(new HashSet<>(1)); + private final Map, Set> internalTriggers; + private final Map, List> classLoadActions = Collections.synchronizedMap(new HashMap<>()); + + // The guest language HotSwap plugin handler passed + // onto us if guest plugins are present at runtime. + private ExternalPluginHandler externalPluginHandler; + + private RedefinitionPluginHandler(EspressoContext espressoContext, Map, Set> triggers) { + this.context = espressoContext; + this.internalTriggers = triggers; + } + + @TruffleBoundary + public void registerClassLoadAction(String className, ClassLoadAction action) { + Symbol type = context.getTypes().fromClassGetName(className); + List list = classLoadActions.get(type); + if (list == null) { + list = Collections.synchronizedList(new ArrayList<>()); + classLoadActions.put(type, list); + } + list.add(action); + } + + public void registerExternalHotSwapHandler(StaticObject handler) { + if (handler != null) { + externalPluginHandler = ExternalPluginHandler.create(handler); + } + } + + public static RedefinitionPluginHandler create(EspressoContext espressoContext) { + // we use ServiceLoader to load all Espresso internal Plugins + ServiceLoader serviceLoader = ServiceLoader.load(InternalRedefinitionPlugin.class); + Iterator pluginIterator = serviceLoader.iterator(); + + Map, Set> triggers = new HashMap<>(); + while (pluginIterator.hasNext()) { + InternalRedefinitionPlugin plugin = pluginIterator.next(); + for (TriggerClass triggerClass : plugin.getTriggerClasses()) { + Symbol triggerType = espressoContext.getTypes().fromClassGetName(triggerClass.getClassName()); + Set triggerClasses = triggers.get(triggerType); + if (triggerClasses == null) { + triggerClasses = new HashSet<>(1); + } + triggerClasses.add(triggerClass); + triggers.put(triggerType, triggerClasses); + } + } + RedefinitionPluginHandler handler = new RedefinitionPluginHandler(espressoContext, triggers); + espressoContext.getRegistries().registerListener(handler); + return handler; + } + + @TruffleBoundary + @Override + public void onClassLoad(ObjectKlass klass) { + // internal plugins + Symbol type = klass.getType(); + if (internalTriggers.containsKey(type)) { + Set triggerClasses = internalTriggers.get(type); + for (TriggerClass triggerClass : triggerClasses) { + if (!internalPlugins.contains(triggerClass.getPlugin())) { + triggerClass.getPlugin().activate(klass.getContext(), this); + internalPlugins.add(triggerClass.getPlugin()); + } + triggerClass.fire(klass); + } + } + // fire registered load actions + List loadActions = classLoadActions.getOrDefault(type, Collections.emptyList()); + Iterator it = loadActions.iterator(); + while (it.hasNext()) { + ClassLoadAction loadAction = it.next(); + loadAction.fire(klass); + it.remove(); + } + if (loadActions.isEmpty()) { + classLoadActions.remove(type); + } + } + + // listener methods + @Override + public boolean rerunClinit(ObjectKlass klass, boolean changed) { + // internal plugins + for (InternalRedefinitionPlugin plugin : internalPlugins) { + if (plugin.reRunClinit(klass, changed)) { + return true; + } + } + // external plugins + if (externalPluginHandler != null) { + return externalPluginHandler.rerunClassInit(klass, changed); + } + return false; + } + + @Override + public void postRedefition(ObjectKlass[] changedKlasses) { + // internal plugins + for (InternalRedefinitionPlugin plugin : internalPlugins) { + plugin.postClassRedefinition(changedKlasses); + } + // external plugins + if (externalPluginHandler != null) { + externalPluginHandler.postHotSwap(changedKlasses); + } + } + + @Override + public void addExtraReloadClasses(List redefineInfos, List additional) { + // internal plugins + for (InternalRedefinitionPlugin plugin : internalPlugins) { + plugin.fillExtraReloadClasses(redefineInfos, additional); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java new file mode 100644 index 000000000000..251b354f5101 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; + +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.runtime.StaticObject; + +public final class UncachedRedefineObject extends RedefineObjectImpl { + public UncachedRedefineObject(StaticObject object) { + super(object); + } + + @Override + public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException { + Method method = lookupMethod(name, args); + if (method != null) { + RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; + for (int i = 0; i < args.length; i++) { + internalArgs[i] = (RedefineObjectImpl) args[i]; + } + return InternalRedefinitionPlugin.createUncached(method.invokeDirect(instance, rawObjects(internalArgs))); + } + throw new NoSuchMethodException(); + } + + @Override + public RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException { + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException("cannot get field on garbage collection instance"); + } + Klass klassRef = klass; + while (klassRef != null) { + for (Field declaredField : klassRef.getDeclaredFields()) { + if (declaredField.getNameAsString().equals(fieldName)) { + return InternalRedefinitionPlugin.createUncached(declaredField.get(theInstance)); + } + } + klassRef = klassRef.getSuperKlass(); + } + throw new NoSuchFieldException(); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java new file mode 100644 index 000000000000..adc37157573c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.jdkproxy; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +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.MethodVariable; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; +import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; + +public class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { + + public static final InteropLibrary INTEROP = InteropLibrary.getUncached(); + + private static final String PROXY_GENERATOR_CLASS = "sun.misc.ProxyGenerator"; + private static final String GENERATOR_METHOD = "generateProxyClass"; + private static final String GENERATOR_METHOD_SIG = "(Ljava/lang/String;[Ljava/lang/Class;I)[B"; + + private final Map> cache = Collections.synchronizedMap(new HashMap<>()); + + private MethodRef proxyGeneratorMethod; + + private ThreadLocal generationInProgress = ThreadLocal.withInitial(() -> false); + + @Override + public String getName() { + return "JDK Dynamic Proxy Reloading Plugin"; + } + + @Override + public TriggerClass[] getTriggerClasses() { + return new TriggerClass[]{new TriggerClass(PROXY_GENERATOR_CLASS, this, klass -> { + // hook into the proxy generator method to obtain proxy generation arguments + hookMethodEntry(klass, new MethodLocator(GENERATOR_METHOD, GENERATOR_METHOD_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { + if (generationInProgress.get()) { + // don't hook when we're re-generating proxy bytes + return; + } + if (proxyGeneratorMethod == null) { + proxyGeneratorMethod = method; + } + collectProxyArguments(variables); + }); + })}; + } + + private synchronized void collectProxyArguments(MethodVariable[] variables) { + Object[] proxyArgs = new Object[3]; + // proxy name + proxyArgs[0] = variables[0].getValue(); + // proxy interfaces + proxyArgs[1] = variables[1].getValue(); + // proxy access modifiers + proxyArgs[2] = variables[2].getValue(); + + try { + // fetch klass instances for the declared proxy interfaces + Object interfaces = proxyArgs[1]; + long arraySize = INTEROP.getArraySize(interfaces); + KlassRef[] proxyInterfaces = new KlassRef[(int) arraySize]; + for (int i = 0; i < arraySize; i++) { + // get the klass type of the interface + proxyInterfaces[i] = getreflectedKlassType(INTEROP.readArrayElement(interfaces, i)); + } + + // register onLoad action that will give us + // the klass object for the generated proxy + String proxyName = proxyArgs[0].toString(); + registerClassLoadAction(proxyName, klass -> { + ProxyCache proxyCache = new ProxyCache(klass, proxyArgs); + + // cache proxy arguments under each interface, so that + // when they change we can re-generate the proxy bytes + for (KlassRef proxyInterface : proxyInterfaces) { + addCacheEntry(proxyCache, proxyInterface); + } + }); + } catch (UnsupportedMessageException | InvalidArrayIndexException e) { + // TODO - log here. Should we have a dedicated HotSwap logger that logs to file? + } + } + + private void addCacheEntry(ProxyCache proxyCache, KlassRef proxyInterface) { + List list = cache.get(proxyInterface); + if (list == null) { + list = Collections.synchronizedList(new ArrayList<>()); + cache.put(proxyInterface, list); + } + list.add(proxyCache); + } + + @Override + @TruffleBoundary + public synchronized void fillExtraReloadClasses(List redefineInfos, List additional) { + for (RedefineInfo redefineInfo : redefineInfos) { + KlassRef klass = redefineInfo.getKlass(); + if (klass != null) { + List list = cache.getOrDefault(klass, Collections.emptyList()); + for (ProxyCache proxyCache : list) { + generationInProgress.set(true); + byte[] proxyBytes = (byte[]) proxyGeneratorMethod.invokeMethod(null, proxyCache.proxyArgs); + generationInProgress.set(false); + additional.add(new RedefineInfo(proxyCache.klass, proxyBytes)); + } + } + } + } + + @Override + public boolean reRunClinit(KlassRef klass, boolean changed) { + // changed Dynamic Proxy classes has cached Method references + // in static fields, so re-run the static initializer + return changed && klass.getNameAsString().contains("$Proxy"); + } + + private final class ProxyCache { + private final KlassRef klass; + private final Object[] proxyArgs; + + ProxyCache(KlassRef klass, Object[] proxyArgs) { + assert proxyArgs.length == 3; + this.klass = klass; + this.proxyArgs = proxyArgs; + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java new file mode 100644 index 000000000000..06d30a81c807 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.micronaut; + +import com.oracle.truffle.espresso.jdwp.api.KlassRef; +import com.oracle.truffle.espresso.jdwp.api.MethodHook; +import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; + +import java.util.ArrayList; + +// TODO - current state doesn't reflect changes to apps properly +// TODO - this needs more thorough integration to Micronaut +public class MicronautPlugin extends InternalRedefinitionPlugin { + + private static final String BEAN_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractBeanDefinitionReference"; + private static final String EXECUTION_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractExecutableMethod"; + private static final String DEFAULT_BEAN_CONTEXT_ACTIVATION_NAME = "io.micronaut.context.DefaultBeanContext"; + private static final String EMBEDDED_APPLICATION_CLASS_NAME = "io.micronaut.runtime.EmbeddedApplication"; + private static final String BEAN_LOCATOR_CLASS_NAME = "io.micronaut.context.BeanLocator"; + + private static final String START_METHOD_NAME = "start"; + private static final String START_METHOD_SIGNATURE = "()Lio/micronaut/context/BeanContext;"; + + private ArrayList clinitRerunTypes; + private boolean needsBeanRefresh; + + // the default bean context + private RedefineObject defaultBeanContext; + // the embedded application instance + private RedefineObject embeddedApplicationType; + + @Override + public String getName() { + return "Micronaut Reloading Plugin"; + } + + @Override + public TriggerClass[] getTriggerClasses() { + ArrayList triggers = new ArrayList<>(3); + triggers.add(new TriggerClass(DEFAULT_BEAN_CONTEXT_ACTIVATION_NAME, this, klass -> { + // default bean class loaded, so register a listener on the start method + // for fetching the context object we need on redefinitions later + hookMethodExit(klass, new MethodLocator(START_METHOD_NAME, START_METHOD_SIGNATURE), MethodHook.Kind.ONE_TIME, (method, returnValue) -> { + defaultBeanContext = InternalRedefinitionPlugin.createCached(returnValue); + }); + })); + triggers.add(new TriggerClass(BEAN_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); + triggers.add(new TriggerClass(EXECUTION_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); + return triggers.toArray(new TriggerClass[triggers.size()]); + } + + private synchronized void addClinitRerunKlass(KlassRef klass) { + if (clinitRerunTypes == null) { + clinitRerunTypes = new ArrayList<>(1); + } + clinitRerunTypes.add(klass); + } + + @Override + public boolean reRunClinit(KlassRef klass, boolean changed) { + if (changed) { + KlassRef superClass = klass.getSuperClass(); + for (KlassRef rerunType : clinitRerunTypes) { + if (rerunType == superClass) { + needsBeanRefresh = true; + return true; + } + } + } + return false; + } + + @Override + public void postClassRedefinition(KlassRef[] changedKlasses) { + if (needsBeanRefresh && defaultBeanContext.notNull()) { + try { + // clear bean caches + flushBeanCaches(); + // fetch needed class types and instances one time + if (embeddedApplicationType == null) { + embeddedApplicationType = defaultBeanContext.fromType(EMBEDDED_APPLICATION_CLASS_NAME); + } + defaultBeanContext.invokeRaw("startEnvironment"); + + // force a bean re-scan: + defaultBeanContext.invokeRaw("readAllBeanConfigurations"); + defaultBeanContext.invokeRaw("readAllBeanDefinitionClasses"); + + // re-wire beans for the application + defaultBeanContext.invokePrecise(BEAN_LOCATOR_CLASS_NAME, "findBean", embeddedApplicationType); + } catch (Throwable ex) { + JDWPLogger.log("Failed to reload Micronaut beans due to %s", JDWPLogger.LogLevel.ALL, ex.getMessage()); + } + } + needsBeanRefresh = false; + } + + private void flushBeanCaches() { + try { + clearCollection(defaultBeanContext, "singletonObjects"); + clearCollection(defaultBeanContext, "scopedProxies"); + clearCollection(defaultBeanContext, "beanDefinitionsClasses"); + clearCollection(defaultBeanContext, "containsBeanCache"); + clearCollection(defaultBeanContext, "initializedObjectsByType"); + clearCollection(defaultBeanContext, "beanConcreteCandidateCache"); + clearCollection(defaultBeanContext, "beanCandidateCache"); + clearCollection(defaultBeanContext, "beanIndex"); + } catch (NoSuchFieldException | NoSuchMethodException ex) { + JDWPLogger.log("Failed to flush Micronaut caches due to %s", JDWPLogger.LogLevel.ALL, ex.getMessage()); + } + } +} 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 5ac5707ceda9..4fb01e129cd8 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 @@ -320,6 +320,10 @@ public String getMultiThreadingDisabledReason() { return multiThreadingDisabled; } + public JDWPContextImpl getJdwpContext() { + return jdwpContext; + } + /** * @return The {@link String}[] array passed to the main function. */ @@ -397,7 +401,10 @@ public void initializeContext() { spawnVM(); this.initialized = true; this.jdwpContext = new JDWPContextImpl(this); - this.eventListener = jdwpContext.jdwpInit(env, getMainThread()); + // enable JDWP instrumenter only if options are set (assumed valid if non-null) + if (JDWPOptions != null) { + this.eventListener = jdwpContext.jdwpInit(env, getMainThread()); + } referenceDrainer.startReferenceDrain(); } @@ -695,7 +702,9 @@ public T trackAllocation(T object) { } public void prepareDispose() { - jdwpContext.finalizeContext(); + if (jdwpContext != null) { + jdwpContext.finalizeContext(); + } } // region Agents 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 6f86d5ed3158..4ff41e977dd7 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 @@ -43,14 +43,9 @@ import com.oracle.truffle.espresso.bytecode.BytecodeStream; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.impl.ArrayKlass; -import com.oracle.truffle.espresso.impl.ChangePacket; -import com.oracle.truffle.espresso.impl.ClassRedefinition; -import com.oracle.truffle.espresso.impl.HotSwapClassInfo; -import com.oracle.truffle.espresso.impl.InnerClassRedefiner; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.impl.ObjectKlass; -import com.oracle.truffle.espresso.impl.RedefintionNotSupportedException; import com.oracle.truffle.espresso.jdwp.api.CallFrame; import com.oracle.truffle.espresso.jdwp.api.FieldRef; import com.oracle.truffle.espresso.jdwp.api.Ids; @@ -72,6 +67,12 @@ import com.oracle.truffle.espresso.nodes.EspressoRootNode; import com.oracle.truffle.espresso.nodes.quick.QuickNode; import com.oracle.truffle.espresso.nodes.quick.interop.ForeignArrayUtils; +import com.oracle.truffle.espresso.redefinition.ChangePacket; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.HotSwapClassInfo; +import com.oracle.truffle.espresso.redefinition.InnerClassRedefiner; +import com.oracle.truffle.espresso.redefinition.RedefintionNotSupportedException; +import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefinitionPluginHandler; import com.oracle.truffle.espresso.substitutions.Target_java_lang_Thread; public final class JDWPContextImpl implements JDWPContext { @@ -83,24 +84,24 @@ public final class JDWPContextImpl implements JDWPContext { private final Ids ids; private JDWPSetup setup; private VMListener eventListener = new EmptyListener(); + private final ClassRedefinition classRedefinition; private InnerClassRedefiner innerClassRedefiner; + private final RedefinitionPluginHandler redefinitionPluginHandler; public JDWPContextImpl(EspressoContext context) { this.context = context; this.ids = new Ids<>(StaticObject.NULL); this.setup = new JDWPSetup(); this.innerClassRedefiner = new InnerClassRedefiner(context); + this.redefinitionPluginHandler = RedefinitionPluginHandler.create(context); + this.classRedefinition = new ClassRedefinition(context, ids, redefinitionPluginHandler); } public VMListener jdwpInit(TruffleLanguage.Env env, Object mainThread) { - // enable JDWP instrumenter only if options are set (assumed valid if non-null) - if (context.JDWPOptions != null) { - Debugger debugger = env.lookup(env.getInstruments().get("debugger"), Debugger.class); - DebuggerController control = env.lookup(env.getInstruments().get(JDWPInstrument.ID), DebuggerController.class); - setup.setup(debugger, control, context.JDWPOptions, this, mainThread); - eventListener = control.getEventListener(); - } - return eventListener; + Debugger debugger = env.lookup(env.getInstruments().get("debugger"), Debugger.class); + DebuggerController control = env.lookup(env.getInstruments().get(JDWPInstrument.ID), DebuggerController.class); + setup.setup(debugger, control, context.JDWPOptions, this, mainThread); + return eventListener = control.getEventListener(); } public void finalizeContext() { @@ -696,55 +697,26 @@ public Node getInstrumentableNode(Node node) { } @Override - public synchronized int redefineClasses(RedefineInfo[] redefineInfos) { + public synchronized int redefineClasses(List redefineInfos) { try { - JDWPLogger.log("Redefining %d classes", JDWPLogger.LogLevel.REDEFINE, redefineInfos.length); - // list of sub classes that needs to refresh things like vtable - List refreshSubClasses = new ArrayList<>(); - - // match anon inner classes with previous state - List removedInnerClasses = new ArrayList<>(0); - HotSwapClassInfo[] matchedInfos = innerClassRedefiner.matchAnonymousInnerClasses(redefineInfos, removedInnerClasses); - - // detect all changes to all classes, throws if redefinition cannot be completed - // due to the nature of the changes - List changePackets = ClassRedefinition.detectClassChanges(matchedInfos, context); - - // We have to redefine super classes prior to subclasses - Collections.sort(changePackets, new HierarchyComparator()); - // begin redefine transaction ClassRedefinition.begin(); - for (ChangePacket packet : changePackets) { - JDWPLogger.log("Redefining class %s", JDWPLogger.LogLevel.REDEFINE, packet.info.getNewName()); - int result = ClassRedefinition.redefineClass(packet, getIds(), context, refreshSubClasses); - if (result != 0) { - return result; - } - } - // refresh subclasses when needed - Collections.sort(refreshSubClasses, new SubClassHierarchyComparator()); - for (ObjectKlass subKlass : refreshSubClasses) { - JDWPLogger.log("Updating sub class %s for redefined super class", JDWPLogger.LogLevel.REDEFINE, subKlass.getName()); - subKlass.onSuperKlassUpdate(); - } - // update the JWDP IDs - for (ChangePacket changePacket : changePackets) { - if (changePacket.info.isRenamed()) { - ObjectKlass klass = changePacket.info.getKlass(); - if (klass != null) { - ids.updateId(klass); - } - } - } + // list to collect all changed classes + List changedKlasses = new ArrayList<>(redefineInfos.size()); - // tell the InnerClassRedefiner to commit the changes to cache - innerClassRedefiner.commit(matchedInfos); + // redefine classes based on direct code changes first + doRedefine(redefineInfos, changedKlasses); - for (ObjectKlass removed : removedInnerClasses) { - removed.removeByRedefinition(); - } + // Now, collect additional classes to redefine in response + // to the redefined classes above + List additional = Collections.synchronizedList(new ArrayList<>()); + classRedefinition.addExtraReloadClasses(redefineInfos, additional); + // redefine additional classes now + doRedefine(additional, changedKlasses); + + // run post redefinition plugins + classRedefinition.runPostRedefintionListeners(changedKlasses.toArray(new ObjectKlass[changedKlasses.size()])); } catch (RedefintionNotSupportedException ex) { return ex.getErrorCode(); } finally { @@ -753,6 +725,63 @@ public synchronized int redefineClasses(RedefineInfo[] redefineInfos) { return 0; } + private void doRedefine(List redefineInfos, List changedKlasses) throws RedefintionNotSupportedException { + // list to hold removed inner classes that must be marked removed + List removedInnerClasses = new ArrayList<>(0); + // list of sub classes that needs to refresh things like vtable + List refreshSubClasses = new ArrayList<>(); + + // match anon inner classes with previous state + HotSwapClassInfo[] matchedInfos = innerClassRedefiner.matchAnonymousInnerClasses(redefineInfos, removedInnerClasses); + + // detect all changes to all classes, throws if redefinition cannot be completed + // due to the nature of the changes + List changePackets = classRedefinition.detectClassChanges(matchedInfos); + + // We have to redefine super classes prior to subclasses + Collections.sort(changePackets, new HierarchyComparator()); + + for (ChangePacket packet : changePackets) { + JDWPLogger.log("Redefining extra class %s", JDWPLogger.LogLevel.REDEFINE, packet.info.getNewName()); + int result = classRedefinition.redefineClass(packet, refreshSubClasses); + if (result != 0) { + throw new RedefintionNotSupportedException(result); + } + } + + // refresh subclasses when needed + Collections.sort(refreshSubClasses, new SubClassHierarchyComparator()); + for (ObjectKlass subKlass : refreshSubClasses) { + JDWPLogger.log("Updating sub class %s for redefined super class", JDWPLogger.LogLevel.REDEFINE, subKlass.getName()); + subKlass.onSuperKlassUpdate(); + } + + // include updated subclasses in all changed classes list + changedKlasses.addAll(refreshSubClasses); + + // update the JWDP IDs for renamed inner classes + for (ChangePacket changePacket : changePackets) { + ObjectKlass klass = changePacket.info.getKlass(); + changedKlasses.add(klass); + if (changePacket.info.isRenamed()) { + if (klass != null) { + ids.updateId(klass); + } + } + } + + // tell the InnerClassRedefiner to commit the changes to cache + innerClassRedefiner.commit(matchedInfos); + + for (ObjectKlass removed : removedInnerClasses) { + removed.removeByRedefinition(); + } + } + + public void registerExternalHotSwapHandler(StaticObject handler) { + redefinitionPluginHandler.registerExternalHotSwapHandler(handler); + } + private static class HierarchyComparator implements Comparator { public int compare(ChangePacket packet1, ChangePacket packet2) { Klass k1 = packet1.info.getKlass(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java new file mode 100644 index 000000000000..ed9d17b8b7a6 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021, 2021, 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.substitutions; + +import com.oracle.truffle.espresso.runtime.StaticObject; + +@EspressoSubstitutions +public final class Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler { + + @Substitution + public static boolean registerHandler(@Host(Object.class) StaticObject handler) { + assert handler != null; + try { + handler.getKlass().getContext().getJdwpContext().registerExternalHotSwapHandler(handler); + } catch (IllegalArgumentException ex) { + return false; + } + return true; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_reflect_NativeMethodAccessorImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_reflect_NativeMethodAccessorImpl.java index 93742c50120b..f93a2bcc964a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_reflect_NativeMethodAccessorImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_reflect_NativeMethodAccessorImpl.java @@ -26,7 +26,7 @@ import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Name; import com.oracle.truffle.espresso.descriptors.Symbol.Type; -import com.oracle.truffle.espresso.impl.ClassRedefinition; +import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ObjectKlass; From accf3926fac1f4db93f8f2f6e0db42d981156add Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 15 Mar 2021 11:00:29 +0100 Subject: [PATCH 010/290] use proper isAssignableFrom to test if a class is really a Dynamic Proxy before rerunning clinit --- .../jdkproxy/JDKProxyRedefinitionPlugin.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index adc37157573c..0e4bbcb18d8a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -49,9 +49,12 @@ public class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { private static final String GENERATOR_METHOD = "generateProxyClass"; private static final String GENERATOR_METHOD_SIG = "(Ljava/lang/String;[Ljava/lang/Class;I)[B"; + private static final String PROXY_SUPER_CLASS = "java.lang.reflect.Proxy"; + private final Map> cache = Collections.synchronizedMap(new HashMap<>()); private MethodRef proxyGeneratorMethod; + private KlassRef proxySuperKlass; private ThreadLocal generationInProgress = ThreadLocal.withInitial(() -> false); @@ -62,7 +65,9 @@ public String getName() { @Override public TriggerClass[] getTriggerClasses() { - return new TriggerClass[]{new TriggerClass(PROXY_GENERATOR_CLASS, this, klass -> { + TriggerClass[] triggerClasses = new TriggerClass[2]; + // trigger on proxy generator class and add generator method hooks + triggerClasses[0] = new TriggerClass(PROXY_GENERATOR_CLASS, this, klass -> { // hook into the proxy generator method to obtain proxy generation arguments hookMethodEntry(klass, new MethodLocator(GENERATOR_METHOD, GENERATOR_METHOD_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { if (generationInProgress.get()) { @@ -74,7 +79,10 @@ public TriggerClass[] getTriggerClasses() { } collectProxyArguments(variables); }); - })}; + }); + // trigger on Proxy super class to obtain the type + triggerClasses[1] = new TriggerClass(PROXY_SUPER_CLASS, this, klass -> proxySuperKlass = klass); + return triggerClasses; } private synchronized void collectProxyArguments(MethodVariable[] variables) { @@ -109,7 +117,7 @@ private synchronized void collectProxyArguments(MethodVariable[] variables) { } }); } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - // TODO - log here. Should we have a dedicated HotSwap logger that logs to file? + // TODO - log here. Should we have a dedicated HotSwap logger that logs to file? } } @@ -143,7 +151,7 @@ public synchronized void fillExtraReloadClasses(List redefineInfos public boolean reRunClinit(KlassRef klass, boolean changed) { // changed Dynamic Proxy classes has cached Method references // in static fields, so re-run the static initializer - return changed && klass.getNameAsString().contains("$Proxy"); + return changed && proxySuperKlass.isAssignable(klass); } private final class ProxyCache { From 8c91c78990a335ffb66370f42972ef1b6afe8a7a Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 17 Mar 2021 13:11:00 +0100 Subject: [PATCH 011/290] various fixes to redefinition internal API --- .../truffle/espresso/jdwp/api/MethodHook.java | 4 + .../truffle/espresso/jdwp/api/MethodRef.java | 3 + .../espresso/jdwp/impl/SocketConnection.java | 2 +- .../jdwp/impl/VMEventListenerImpl.java | 8 +- .../oracle/truffle/espresso/impl/Klass.java | 2 +- .../oracle/truffle/espresso/impl/Method.java | 28 ++++++ .../truffle/espresso/impl/ObjectKlass.java | 10 +- .../api/InternalRedefinitionPlugin.java | 10 +- .../plugins/api/RedefineObject.java | 5 +- .../plugins/api/RedefintionHook.java | 8 ++ .../plugins/impl/CachedRedefineObject.java | 4 +- .../plugins/impl/RedefineObjectImpl.java | 72 +++++++++++--- .../plugins/impl/UncachedRedefineObject.java | 4 +- .../jdkproxy/JDKProxyRedefinitionPlugin.java | 3 - .../plugins/micronaut/MicronautPlugin.java | 98 +++++++++---------- 15 files changed, 178 insertions(+), 83 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java index d7420f5ed6d9..6cc2a48c0a5e 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodHook.java @@ -38,4 +38,8 @@ default int getRequestId() { boolean onMethodEnter(MethodRef method, MethodVariable[] variables); boolean onMethodExit(MethodRef method, Object returnValue); + + default boolean hasFired() { + return false; + } } 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 f2cdcce9172a..d83c3b4e7671 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 @@ -195,6 +195,9 @@ public interface MethodRef { */ void removedMethodHook(int requestId); + + void removedMethodHook(MethodHook hook); + /** * Determines if there are any breakpoints set on this method. * diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/SocketConnection.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/SocketConnection.java index 6b354fc33323..6634a94b52af 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/SocketConnection.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/SocketConnection.java @@ -40,7 +40,7 @@ public final class SocketConnection implements Runnable { private final Object sendLock = new Object(); private final Object closeLock = new Object(); - private final BlockingQueue queue = new ArrayBlockingQueue<>(512); + private final BlockingQueue queue = new ArrayBlockingQueue<>(4096); SocketConnection(Socket socket, ServerSocket serverSocket) throws IOException { this.socket = socket; diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java index c8a964b38d39..e51ae017c1f1 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/VMEventListenerImpl.java @@ -182,7 +182,9 @@ public boolean onMethodEntry(MethodRef method, Object scope) { } switch (hook.getKind()) { case ONE_TIME: - method.removedMethodHook(-1); + if (hook.hasFired()) { + method.removedMethodHook(hook); + } break; case INDEFINITE: // leave the hook active @@ -205,7 +207,9 @@ public boolean onMethodReturn(MethodRef method, Object returnValue) { } switch (hook.getKind()) { case ONE_TIME: - method.removedMethodHook(-1); + if (hook.hasFired()) { + method.removedMethodHook(hook); + } break; case INDEFINITE: // leave the hook active diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index 06fc1d8f2fb4..18909f71ad3a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -1013,7 +1013,7 @@ int getHierarchyDepth() { @CompilationFinal(dimensions = 1) private Klass[] transitiveInterfaceCache; - protected final Klass[] getTransitiveInterfacesList() { + public final Klass[] getTransitiveInterfacesList() { Klass[] transitiveInterfaces = transitiveInterfaceCache; if (transitiveInterfaces == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); 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 1bdafd6bb0e5..116f1154cbeb 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 @@ -1043,6 +1043,29 @@ public void removeActiveHook(int requestId) { } } + public void removeActiveHook(MethodHook hook) { + // shrink the array to avoid null values + if (hooks.length == 0) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); + } else if (hooks.length == 1) { + hooks = new MethodHook[0]; + hasActiveHook.set(false); + } else { + int removeIndex = -1; + for (int i = 0; i < hooks.length; i++) { + if (hooks[i] == hook) { + removeIndex = i; + break; + } + } + 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; + } + } + public SharedRedefinitionContent redefine(ParserMethod newMethod, ParserKlass newKlass, Ids ids) { // invalidate old version // install the new method version immediately @@ -1387,6 +1410,11 @@ public void removedMethodHook(int requestId) { getMethod().removeActiveHook(requestId); } + @Override + public void removedMethodHook(MethodHook hook) { + getMethod().removeActiveHook(hook); + } + @Override public boolean hasActiveHook() { return getMethod().hasActiveHook(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index e6d1f6944f0a..6a30ca75f30b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1195,11 +1195,7 @@ public void redefineClass(ChangePacket packet, List refreshSubClass klassVersion = new KlassVersion(pool, linkedKlass, newDeclaredMethods, mirandaMethods, vtable, itable, iKlassTable); - // flush caches before invalidating to avoid races - // a potential thread fetching new reflection data - // will be blocked at entry until the redefinition - // transaction is ended - flushReflectionCaches(); + incrementKlassRedefinitionCount(); oldVersion.assumption.invalidate(); } @@ -1222,7 +1218,7 @@ private static void checkCopyMethods(Method method, Method[] table, Method.Share } } - private void flushReflectionCaches() { + private void incrementKlassRedefinitionCount() { // increment the redefine count on the class instance to flush reflection caches int value = InterpreterToVM.getFieldInt(mirror(), getMeta().java_lang_Class_classRedefinedCount); InterpreterToVM.setFieldInt(++value, mirror(), getMeta().java_lang_Class_classRedefinedCount); @@ -1273,7 +1269,7 @@ public void onSuperKlassUpdate() { // a potential thread fetching new reflection data // will be blocked at entry until the redefinition // transaction is ended - flushReflectionCaches(); + incrementKlassRedefinitionCount(); oldVersion.assumption.invalidate(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java index 02dc68091ffd..7402f0ce2b6d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java @@ -25,6 +25,8 @@ import java.util.Collection; import java.util.List; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; @@ -38,6 +40,8 @@ public abstract class InternalRedefinitionPlugin { + protected static final InteropLibrary INTEROP = InteropLibrary.getUncached(); + private static final String constructorName = ""; private EspressoContext context; @@ -84,6 +88,10 @@ protected KlassRef getreflectedKlassType(Object classObject) { return context.getJdwpContext().getReflectedType(classObject); } + protected Object getGuestClassInstance(KlassRef klass) { + return ((Klass) klass).mirror(); + } + protected void registerClassLoadAction(String className, ClassLoadAction action) { redefinitionPluginHandler.registerClassLoadAction(className, action); } @@ -117,7 +125,7 @@ protected void hookConstructor(KlassRef klass, MethodHook.Kind kind, MethodEntry protected void clearCollection(@Host(Collection.class) RedefineObject object, String fieldName) throws NoSuchFieldException, NoSuchMethodException { RedefineObject collectionField = object.getInstanceField(fieldName); if (collectionField != null) { - collectionField.invokeRaw("clear"); + collectionField.invoke("clear"); } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java index 60657c5cc659..46ea8cb42a33 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java @@ -31,9 +31,12 @@ public interface RedefineObject { RedefineObject fromType(String className); - RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException; + Object invoke(String name, RedefineObject... args) throws NoSuchMethodException; RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException; RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; + + Object getRawValue(); + } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java index 87a5c622113b..c69f8d710e50 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java @@ -31,6 +31,7 @@ public final class RedefintionHook implements MethodHook { private final MethodExitHook onExitHook; private final MethodEntryHook onEntryHook; private final Kind kind; + private boolean hasFired = false; public RedefintionHook(MethodEntryHook onEntryHook, Kind kind) { this.onExitHook = null; @@ -53,6 +54,7 @@ public Kind getKind() { public boolean onMethodEnter(MethodRef method, MethodVariable[] variables) { if (onEntryHook != null) { onEntryHook.onMethodEnter(method, variables); + hasFired = true; } return false; } @@ -61,7 +63,13 @@ public boolean onMethodEnter(MethodRef method, MethodVariable[] variables) { public boolean onMethodExit(MethodRef method, Object returnValue) { if (onExitHook != null) { onExitHook.onMethodExit(method, returnValue); + hasFired = true; } return false; } + + @Override + public boolean hasFired() { + return hasFired; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java index de4b5069f218..52bee0103d4a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java @@ -53,7 +53,7 @@ public CachedRedefineObject(KlassRef klass) { @Override @TruffleBoundary - public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException { + public Object invoke(String name, RedefineObject... args) throws NoSuchMethodException { StringBuilder stringBuffer = new StringBuilder(name); for (RedefineObject arg : args) { stringBuffer.append(((RedefineObjectImpl) arg).instance.get().getKlass().getNameAsString()); @@ -75,7 +75,7 @@ public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSu for (int i = 0; i < args.length; i++) { internalArgs[i] = (RedefineObjectImpl) args[i]; } - return InternalRedefinitionPlugin.createUncached(method.invokeDirect(theInstance, rawObjects(internalArgs))); + return method.invokeDirect(theInstance, rawObjects(internalArgs)); } throw new NoSuchMethodException(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java index 66de8d0f2491..efb9ddf1b114 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java @@ -25,7 +25,6 @@ import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -54,7 +53,12 @@ public Klass getKlass() { } @Override - public abstract RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException; + public Object getRawValue() { + return instance.get(); + } + + @Override + public abstract Object invoke(String name, RedefineObject... args) throws NoSuchMethodException; @SuppressWarnings("unused") public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException { @@ -64,10 +68,10 @@ public RedefineObject invokePrecise(String className, String methodName, Redefin @Override public abstract RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; - protected Method lookupMethod(String name, RedefineObject[] args) throws NoSuchMethodException { + protected Method lookupMethod(String name, RedefineObject[] args) { StaticObject theInstance = instance.get(); if (theInstance == null) { - throw new IllegalStateException("cannot invoke method on garbage collection instance"); + throw new IllegalStateException("cannot invoke method on garbage collected instance"); } Klass currentKlass = klass; @@ -77,8 +81,8 @@ protected Method lookupMethod(String name, RedefineObject[] args) throws NoSuchM if (method != null) { return method; } - ObjectKlass[] interfaces = currentKlass.getInterfaces(); - for (ObjectKlass itf : interfaces) { + Klass[] interfaces = currentKlass.getTransitiveInterfacesList(); + for (Klass itf : interfaces) { method = lookupMethod(itf, name, args); if (method != null && !method.isAbstract()) { return method; @@ -89,22 +93,68 @@ protected Method lookupMethod(String name, RedefineObject[] args) throws NoSuchM return null; } - protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args) throws NoSuchMethodException { + protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args) { for (Method declaredMethod : klassRef.getDeclaredMethods()) { if (declaredMethod.getNameAsString().equals(name)) { // match arguments boolean match = true; + Klass[] parameters = declaredMethod.resolveParameterKlasses(); if (declaredMethod.getParameterCount() == args.length) { - Klass[] parameters = declaredMethod.resolveParameterKlasses(); for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + if (parameters[i].isPrimitive()) { + match = false; + break; + } + continue; + } if (!parameters[i].isAssignableFrom((Klass) args[i].getKlass())) { match = false; break; } } } else if (declaredMethod.isVarargs()) { - // TODO - not implemented yet - throw new NoSuchMethodException("varargs lookup not implemented"); + int parameterCount = declaredMethod.getParameterCount(); + int argsCount = args.length; + + if (parameterCount == 1) { + // pure varargs + // TODO - implement this case + match = false; + } else { + // match before varargs param + for (int i = 0; i < parameterCount - 1; i++) { + if (args[i] == null) { + if (parameters[i].isPrimitive()) { + match = false; + break; + } + continue; + } + if (!parameters[i].isAssignableFrom((Klass) args[i].getKlass())) { + match = false; + break; + } + } + // match varargs + Klass varargsParam = parameters[parameterCount - 1]; + for (int i = parameterCount - 1; i < argsCount; i++) { + if (args[i] == null) { + if (varargsParam.isPrimitive()) { + match = false; + break; + } + continue; + } + if (varargsParam.isAssignableFrom((Klass) args[i].getKlass())) { + match = false; + break; + } + } + } + } else { + // arguments didn't match + match = false; } if (match) { return declaredMethod; @@ -117,7 +167,7 @@ protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args protected Object[] rawObjects(RedefineObjectImpl[] args) { Object[] result = new Object[args.length]; for (int i = 0; i < args.length; i++) { - result[i] = args[i].instance; + result[i] = args[i].instance.get(); } return result; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java index 251b354f5101..4b5729388ddc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java @@ -35,14 +35,14 @@ public UncachedRedefineObject(StaticObject object) { } @Override - public RedefineObject invokeRaw(String name, RedefineObject... args) throws NoSuchMethodException { + public Object invoke(String name, RedefineObject... args) throws NoSuchMethodException { Method method = lookupMethod(name, args); if (method != null) { RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; for (int i = 0; i < args.length; i++) { internalArgs[i] = (RedefineObjectImpl) args[i]; } - return InternalRedefinitionPlugin.createUncached(method.invokeDirect(instance, rawObjects(internalArgs))); + return method.invokeDirect(instance.get(), rawObjects(internalArgs)); } throw new NoSuchMethodException(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index 0e4bbcb18d8a..267601e63398 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -29,7 +29,6 @@ import java.util.Map; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.espresso.jdwp.api.KlassRef; @@ -43,8 +42,6 @@ public class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { - public static final InteropLibrary INTEROP = InteropLibrary.getUncached(); - private static final String PROXY_GENERATOR_CLASS = "sun.misc.ProxyGenerator"; private static final String GENERATOR_METHOD = "generateProxyClass"; private static final String GENERATOR_METHOD_SIG = "(Ljava/lang/String;[Ljava/lang/Class;I)[B"; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java index 06d30a81c807..b1e1495b673e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java @@ -24,7 +24,6 @@ import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodHook; -import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; @@ -32,26 +31,30 @@ import java.util.ArrayList; -// TODO - current state doesn't reflect changes to apps properly -// TODO - this needs more thorough integration to Micronaut public class MicronautPlugin extends InternalRedefinitionPlugin { private static final String BEAN_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractBeanDefinitionReference"; private static final String EXECUTION_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractExecutableMethod"; - private static final String DEFAULT_BEAN_CONTEXT_ACTIVATION_NAME = "io.micronaut.context.DefaultBeanContext"; - private static final String EMBEDDED_APPLICATION_CLASS_NAME = "io.micronaut.runtime.EmbeddedApplication"; - private static final String BEAN_LOCATOR_CLASS_NAME = "io.micronaut.context.BeanLocator"; + private static final String MICRONAUT_CLASS = "io.micronaut.runtime.Micronaut"; + private static final String ROUTING_IN_BOUND_HANDLER = "io.micronaut.http.server.netty.RoutingInBoundHandler"; + + private static final String RUN = "run"; + private static final String RUN_SIG = "([Ljava/lang/Class;[Ljava/lang/String;)Lio/micronaut/context/ApplicationContext;"; + private static final String STOP = "stop"; + private static final String CHANNEL_READ_0 = "channelRead0"; + private static final String CHANNEL_READ_0_SIG = "(Lio/netty/channel/ChannelHandlerContext;Lio/micronaut/http/HttpRequest;)V"; - private static final String START_METHOD_NAME = "start"; - private static final String START_METHOD_SIGNATURE = "()Lio/micronaut/context/BeanContext;"; private ArrayList clinitRerunTypes; private boolean needsBeanRefresh; // the default bean context - private RedefineObject defaultBeanContext; - // the embedded application instance - private RedefineObject embeddedApplicationType; + private RedefineObject micronautContext; + + private RedefineObject[] runArgs; + private RedefineObject micronautClassInstance; + + private KlassRef rountingHandler; @Override public String getName() { @@ -60,16 +63,29 @@ public String getName() { @Override public TriggerClass[] getTriggerClasses() { - ArrayList triggers = new ArrayList<>(3); - triggers.add(new TriggerClass(DEFAULT_BEAN_CONTEXT_ACTIVATION_NAME, this, klass -> { - // default bean class loaded, so register a listener on the start method - // for fetching the context object we need on redefinitions later - hookMethodExit(klass, new MethodLocator(START_METHOD_NAME, START_METHOD_SIGNATURE), MethodHook.Kind.ONE_TIME, (method, returnValue) -> { - defaultBeanContext = InternalRedefinitionPlugin.createCached(returnValue); + ArrayList triggers = new ArrayList<>(4); + triggers.add(new TriggerClass(MICRONAUT_CLASS, this, klass -> { + // we need the application context when reloading + hookMethodExit(klass, new MethodLocator(RUN, RUN_SIG), MethodHook.Kind.INDEFINITE, (method, returnValue) -> { + micronautContext = InternalRedefinitionPlugin.createCached(returnValue); + }); + // we need the run arguments for re-starting a fresh context + hookMethodEntry(klass, new MethodLocator(RUN, RUN_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { + micronautClassInstance = InternalRedefinitionPlugin.createCached(klass); + // collect run arguments + runArgs = new RedefineObject[2]; + // array of j.l.Class instances + runArgs[0] = InternalRedefinitionPlugin.createUncached(variables[0].getValue()); + // array of String args + runArgs[1] = InternalRedefinitionPlugin.createUncached(variables[1].getValue()); }); })); triggers.add(new TriggerClass(BEAN_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); triggers.add(new TriggerClass(EXECUTION_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); + triggers.add(new TriggerClass(ROUTING_IN_BOUND_HANDLER, this, klass -> { + rountingHandler = klass; + })); + return triggers.toArray(new TriggerClass[triggers.size()]); } @@ -83,9 +99,8 @@ private synchronized void addClinitRerunKlass(KlassRef klass) { @Override public boolean reRunClinit(KlassRef klass, boolean changed) { if (changed) { - KlassRef superClass = klass.getSuperClass(); for (KlassRef rerunType : clinitRerunTypes) { - if (rerunType == superClass) { + if (rerunType.isAssignable(klass)) { needsBeanRefresh = true; return true; } @@ -96,41 +111,20 @@ public boolean reRunClinit(KlassRef klass, boolean changed) { @Override public void postClassRedefinition(KlassRef[] changedKlasses) { - if (needsBeanRefresh && defaultBeanContext.notNull()) { - try { - // clear bean caches - flushBeanCaches(); - // fetch needed class types and instances one time - if (embeddedApplicationType == null) { - embeddedApplicationType = defaultBeanContext.fromType(EMBEDDED_APPLICATION_CLASS_NAME); + if (needsBeanRefresh && micronautContext != null) { + // OK, simple HotSwap is not enough, so register a reload hook + // in the HTTP pipeline that restarts the context on the next + // request. + hookMethodEntry(rountingHandler, new MethodLocator(CHANNEL_READ_0, CHANNEL_READ_0_SIG), MethodHook.Kind.ONE_TIME, (method, variables) -> { + try { + // restart Micronaut application context + micronautContext.invoke(STOP); + micronautClassInstance.invoke("run", runArgs); + } catch (Throwable e) { + e.printStackTrace(); } - defaultBeanContext.invokeRaw("startEnvironment"); - - // force a bean re-scan: - defaultBeanContext.invokeRaw("readAllBeanConfigurations"); - defaultBeanContext.invokeRaw("readAllBeanDefinitionClasses"); - - // re-wire beans for the application - defaultBeanContext.invokePrecise(BEAN_LOCATOR_CLASS_NAME, "findBean", embeddedApplicationType); - } catch (Throwable ex) { - JDWPLogger.log("Failed to reload Micronaut beans due to %s", JDWPLogger.LogLevel.ALL, ex.getMessage()); - } + }); } needsBeanRefresh = false; } - - private void flushBeanCaches() { - try { - clearCollection(defaultBeanContext, "singletonObjects"); - clearCollection(defaultBeanContext, "scopedProxies"); - clearCollection(defaultBeanContext, "beanDefinitionsClasses"); - clearCollection(defaultBeanContext, "containsBeanCache"); - clearCollection(defaultBeanContext, "initializedObjectsByType"); - clearCollection(defaultBeanContext, "beanConcreteCandidateCache"); - clearCollection(defaultBeanContext, "beanCandidateCache"); - clearCollection(defaultBeanContext, "beanIndex"); - } catch (NoSuchFieldException | NoSuchMethodException ex) { - JDWPLogger.log("Failed to flush Micronaut caches due to %s", JDWPLogger.LogLevel.ALL, ex.getMessage()); - } - } } From 3b1fd8eb8d5ae5af88a7fc3bbf50002cdbdbd37e Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 18 Mar 2021 08:40:24 +0100 Subject: [PATCH 012/290] rerun class initializers during HotSwap in a dedicated thread and mark it as a system thread to avoid suspension --- .../espresso/jdwp/api/JDWPContext.java | 2 + .../jdwp/impl/DebuggerController.java | 8 ++- .../redefinition/ClassRedefinition.java | 2 +- .../espresso/runtime/JDWPContextImpl.java | 59 +++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) 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 feb7d7493361..c5c1fef01064 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 @@ -490,4 +490,6 @@ public interface JDWPContext { * @return the nearest instrumentable node */ Node getInstrumentableNode(Node node); + + boolean isSystemThread(Thread hostThread); } 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 06dcd9847146..db3b48ce1c89 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 @@ -759,7 +759,13 @@ private class SuspendedCallbackImpl implements SuspendedCallback { @Override public void onSuspend(SuspendedEvent event) { - Object currentThread = getContext().asGuestThread(Thread.currentThread()); + Thread hostThread = Thread.currentThread(); + if (context.isSystemThread(hostThread)) { + // always allow VM threads to run guest code without + // the risk of being suspended + return; + } + Object currentThread = getContext().asGuestThread(hostThread); JDWPLogger.log("Suspended at: %s in thread: %s", JDWPLogger.LogLevel.STEPPING, event.getSourceSection().toString(), getThreadName(currentThread)); SteppingInfo steppingInfo = commandRequestIds.remove(currentThread); if (steppingInfo != null) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index de4ec17d3fb1..1cd1eff125b8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -664,7 +664,7 @@ private void doRedefineClass(ChangePacket packet, List refreshSubCl } oldKlass.redefineClass(packet, refreshSubClasses, ids); if (redefineListener.rerunClinit(oldKlass, packet.detectedChange.clinitChanged())) { - oldKlass.reRunClinit(); + context.getJdwpContext().rerunclinit(oldKlass); } } 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 4ff41e977dd7..ff292f29bdf9 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 @@ -29,6 +29,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.regex.Matcher; import com.oracle.truffle.api.TruffleLanguage; @@ -87,6 +89,8 @@ public final class JDWPContextImpl implements JDWPContext { private final ClassRedefinition classRedefinition; private InnerClassRedefiner innerClassRedefiner; private final RedefinitionPluginHandler redefinitionPluginHandler; + private Thread reloaderThread; + private final BlockingQueue queue = new ArrayBlockingQueue<>(128); public JDWPContextImpl(EspressoContext context) { this.context = context; @@ -95,6 +99,7 @@ public JDWPContextImpl(EspressoContext context) { this.innerClassRedefiner = new InnerClassRedefiner(context); this.redefinitionPluginHandler = RedefinitionPluginHandler.create(context); this.classRedefinition = new ClassRedefinition(context, ids, redefinitionPluginHandler); + initializeReloaderThread(); } public VMListener jdwpInit(TruffleLanguage.Env env, Object mainThread) { @@ -696,6 +701,47 @@ public Node getInstrumentableNode(Node node) { return null; } + @Override + public boolean isSystemThread(Thread hostThread) { + return hostThread == reloaderThread; + } + + private void initializeReloaderThread() { + reloaderThread = new Thread(new Runnable() { + @Override + public void run() { + while (!reloaderThread.isInterrupted()) { + try { + queue.take().fire(); + } catch (InterruptedException e) { + reloaderThread.interrupt(); + } catch (Throwable t) { + // while rerunning class initializers + // in the guest, some anomalies are to + // be expected. Treat those as non-fatal + // TODO - add logging + } + } + } + }, "reloader"); + reloaderThread.start(); + } + + public void rerunclinit(ObjectKlass oldKlass) { + queue.add(new ReloadingAction(oldKlass)); + } + + public void ensureReloadingDone() { + while (!queue.isEmpty()) { + // poll at intervals + try { + Thread.sleep(10); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + @Override public synchronized int redefineClasses(List redefineInfos) { try { @@ -827,4 +873,17 @@ public int compare(ObjectKlass k1, ObjectKlass k2) { return 0; } } + + private class ReloadingAction { + private ObjectKlass klass; + + private ReloadingAction(ObjectKlass klass) { + this.klass = klass; + } + + private void fire() { + System.out.println("running clinit action for klass: " + klass); + klass.reRunClinit(); + } + } } From e317a7ee60ee16c83b62a01ad0bf1e73438594e3 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 6 Apr 2021 08:20:49 +0200 Subject: [PATCH 013/290] Add API for registering post HotSwap actions. Static class re-init now checks hierarchy when deciding whether to re-run clinit --- .../espresso/hotswap/EspressoHotSwap.java | 6 +++++ .../espresso/hotswap/HotSwapHandler.java | 25 ++++++++++++++++--- .../espresso/runtime/JDWPContextImpl.java | 1 - 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java index c8f11f5f23c3..7ddde00f2b0b 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -57,6 +57,12 @@ public static void registerPlugin(HotSwapPlugin plugin) { } } + public static void registerPostHotSwapAction(HotSwapAction action) { + if (handler != null) { + handler.registerPostHotSwapAction(action); + } + } + public static void registerHotSwapAction(Class klass, HotSwapAction action) { if (handler != null) { handler.registerHotSwapAction(klass, action); diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java index 4a4bee7ff740..1997b2f7fb12 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java @@ -40,9 +40,11 @@ */ package com.oracle.truffle.espresso.hotswap; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -52,6 +54,7 @@ final class HotSwapHandler { private final Set plugins = Collections.synchronizedSet(new HashSet<>()); private final Map, Set> hotSwapActions = new HashMap<>(); + private final List postHotSwapActions = Collections.synchronizedList(new ArrayList<>()); private final Map, Boolean> staticInitializerHotSwap = new HashMap<>(); private HotSwapHandler() { @@ -77,12 +80,16 @@ public void registerHotSwapAction(Class klass, HotSwapAction action) { hotSwapActions.get(klass).add(action); } + public void registerPostHotSwapAction(HotSwapAction action) { + postHotSwapActions.add(action); + } + public void registerStaticClassInitHotSwap(Class klass, boolean onChange) { - if (!staticInitializerHotSwap.containsKey(klass)) { - staticInitializerHotSwap.put(klass, onChange); - } else if (!onChange) { + if (!staticInitializerHotSwap.containsKey(klass)) { + staticInitializerHotSwap.put(klass, onChange); + } else if (!onChange) { staticInitializerHotSwap.put(klass, false); - } + } } @SuppressWarnings("unused") @@ -96,6 +103,8 @@ public void postHotSwap(Class[] changedClasses) { for (HotSwapPlugin plugin : plugins) { plugin.postHotSwap(); } + // fire all registered post HotSwap actions + postHotSwapActions.forEach(HotSwapAction::onHotSwap); } @SuppressWarnings("unused") @@ -103,6 +112,14 @@ public boolean rerunClassInit(Class klass, boolean changed) { if (staticInitializerHotSwap.containsKey(klass)) { boolean onlyOnChange = staticInitializerHotSwap.get(klass); return !onlyOnChange || changed; + } else { + // check class hierarchy + for (Map.Entry, Boolean> entry : staticInitializerHotSwap.entrySet()) { + Class key = entry.getKey(); + if (key.isAssignableFrom(klass)) { + return !entry.getValue() || changed; + } + } } return false; } 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 ff292f29bdf9..460ab31a722b 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 @@ -882,7 +882,6 @@ private ReloadingAction(ObjectKlass klass) { } private void fire() { - System.out.println("running clinit action for klass: " + klass); klass.reRunClinit(); } } From cd0364f6f397080371ece2ac337d5afef536820a Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 7 Apr 2021 14:30:10 +0200 Subject: [PATCH 014/290] remove internal Micronaut plugin --- .../plugins/micronaut/MicronautPlugin.java | 130 ------------------ 1 file changed, 130 deletions(-) delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java deleted file mode 100644 index b1e1495b673e..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/micronaut/MicronautPlugin.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.micronaut; - -import com.oracle.truffle.espresso.jdwp.api.KlassRef; -import com.oracle.truffle.espresso.jdwp.api.MethodHook; -import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; -import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; -import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; -import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; - -import java.util.ArrayList; - -public class MicronautPlugin extends InternalRedefinitionPlugin { - - private static final String BEAN_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractBeanDefinitionReference"; - private static final String EXECUTION_CLASS_ACTIVATION_NAME = "io.micronaut.context.AbstractExecutableMethod"; - private static final String MICRONAUT_CLASS = "io.micronaut.runtime.Micronaut"; - private static final String ROUTING_IN_BOUND_HANDLER = "io.micronaut.http.server.netty.RoutingInBoundHandler"; - - private static final String RUN = "run"; - private static final String RUN_SIG = "([Ljava/lang/Class;[Ljava/lang/String;)Lio/micronaut/context/ApplicationContext;"; - private static final String STOP = "stop"; - private static final String CHANNEL_READ_0 = "channelRead0"; - private static final String CHANNEL_READ_0_SIG = "(Lio/netty/channel/ChannelHandlerContext;Lio/micronaut/http/HttpRequest;)V"; - - - private ArrayList clinitRerunTypes; - private boolean needsBeanRefresh; - - // the default bean context - private RedefineObject micronautContext; - - private RedefineObject[] runArgs; - private RedefineObject micronautClassInstance; - - private KlassRef rountingHandler; - - @Override - public String getName() { - return "Micronaut Reloading Plugin"; - } - - @Override - public TriggerClass[] getTriggerClasses() { - ArrayList triggers = new ArrayList<>(4); - triggers.add(new TriggerClass(MICRONAUT_CLASS, this, klass -> { - // we need the application context when reloading - hookMethodExit(klass, new MethodLocator(RUN, RUN_SIG), MethodHook.Kind.INDEFINITE, (method, returnValue) -> { - micronautContext = InternalRedefinitionPlugin.createCached(returnValue); - }); - // we need the run arguments for re-starting a fresh context - hookMethodEntry(klass, new MethodLocator(RUN, RUN_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { - micronautClassInstance = InternalRedefinitionPlugin.createCached(klass); - // collect run arguments - runArgs = new RedefineObject[2]; - // array of j.l.Class instances - runArgs[0] = InternalRedefinitionPlugin.createUncached(variables[0].getValue()); - // array of String args - runArgs[1] = InternalRedefinitionPlugin.createUncached(variables[1].getValue()); - }); - })); - triggers.add(new TriggerClass(BEAN_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); - triggers.add(new TriggerClass(EXECUTION_CLASS_ACTIVATION_NAME, this, klass -> addClinitRerunKlass(klass))); - triggers.add(new TriggerClass(ROUTING_IN_BOUND_HANDLER, this, klass -> { - rountingHandler = klass; - })); - - return triggers.toArray(new TriggerClass[triggers.size()]); - } - - private synchronized void addClinitRerunKlass(KlassRef klass) { - if (clinitRerunTypes == null) { - clinitRerunTypes = new ArrayList<>(1); - } - clinitRerunTypes.add(klass); - } - - @Override - public boolean reRunClinit(KlassRef klass, boolean changed) { - if (changed) { - for (KlassRef rerunType : clinitRerunTypes) { - if (rerunType.isAssignable(klass)) { - needsBeanRefresh = true; - return true; - } - } - } - return false; - } - - @Override - public void postClassRedefinition(KlassRef[] changedKlasses) { - if (needsBeanRefresh && micronautContext != null) { - // OK, simple HotSwap is not enough, so register a reload hook - // in the HTTP pipeline that restarts the context on the next - // request. - hookMethodEntry(rountingHandler, new MethodLocator(CHANNEL_READ_0, CHANNEL_READ_0_SIG), MethodHook.Kind.ONE_TIME, (method, variables) -> { - try { - // restart Micronaut application context - micronautContext.invoke(STOP); - micronautClassInstance.invoke("run", runArgs); - } catch (Throwable e) { - e.printStackTrace(); - } - }); - } - needsBeanRefresh = false; - } -} From 0778f05579fc69d1ca6fcc61ce513203e40e66b3 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 7 Apr 2021 14:31:47 +0200 Subject: [PATCH 015/290] add new internal JDK introspector and bean cache flushing plugin --- ...ion.plugins.api.InternalRedefinitionPlugin | 2 +- .../jdkcaches/JDKCacheRedefinitionPlugin.java | 85 +++++++++++++++++++ .../jdkproxy/JDKProxyRedefinitionPlugin.java | 2 +- 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin index a03c21cb2019..46bb33322bc8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin +++ b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin @@ -1,2 +1,2 @@ -com.oracle.truffle.espresso.redefinition.plugins.micronaut.MicronautPlugin com.oracle.truffle.espresso.redefinition.plugins.jdkproxy.JDKProxyRedefinitionPlugin +com.oracle.truffle.espresso.redefinition.plugins.jdkcaches.JDKCacheRedefinitionPlugin diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java new file mode 100644 index 000000000000..ccb4effcce84 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition.plugins.jdkcaches; + +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.redefinition.plugins.api.InternalRedefinitionPlugin; +import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; +import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; +import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class JDKCacheRedefinitionPlugin extends InternalRedefinitionPlugin { + + public static final String INTROSPECTOR_CLASS = "java.beans.Introspector"; + public static final String FLUSH_CACHES_METHOD = "flushFromCaches"; + public static final String FLUSH_CACHES_SIG = "(Ljava/lang/Class;)V"; + private MethodRef flushFromCachesMethod; + + public static final String THREAD_GROUP_CONTEXT = "java.beans.ThreadGroupContext"; + public static final String REMOVE_BEAN_INFO = "removeBeanInfo"; + private List threadGroupContext = Collections.synchronizedList(new ArrayList<>(1)); + + @Override + public String getName() { + return "JDK Cache Flushing Plugin"; + } + + @Override + public TriggerClass[] getTriggerClasses() { + TriggerClass[] triggerClasses = new TriggerClass[2]; + triggerClasses[0] = new TriggerClass(INTROSPECTOR_CLASS, this, klass -> { + hookMethodEntry(klass, new MethodLocator(FLUSH_CACHES_METHOD, FLUSH_CACHES_SIG), MethodHook.Kind.ONE_TIME, + ((method, variables) -> flushFromCachesMethod = method)); + }); + triggerClasses[1] = new TriggerClass(THREAD_GROUP_CONTEXT, this, klass -> { + hookConstructor(klass, MethodHook.Kind.INDEFINITE, ((method, variables) -> { + threadGroupContext.add(InternalRedefinitionPlugin.createCached(variables[0].getValue())); + })); + }); + return triggerClasses; + } + + @Override + public void postClassRedefinition(KlassRef[] changedKlasses) { + for (KlassRef changedKlass : changedKlasses) { + Object guestKlass = getGuestClassInstance(changedKlass); + if (flushFromCachesMethod != null) { + flushFromCachesMethod.invokeMethod(null, new Object[]{guestKlass}); + } + for (RedefineObject context : threadGroupContext) { + try { + context.invoke(REMOVE_BEAN_INFO, InternalRedefinitionPlugin.createUncached(guestKlass)); + } catch (NoSuchMethodException e) { + // TODO - add logging + } + } + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index 267601e63398..1e4eed94d824 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -40,7 +40,7 @@ import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; -public class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { +public final class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { private static final String PROXY_GENERATOR_CLASS = "sun.misc.ProxyGenerator"; private static final String GENERATOR_METHOD = "generateProxyClass"; From 9d34ec1ae9cd294de62bec1b3daa28bc30dd6f58 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 7 Apr 2021 14:33:50 +0200 Subject: [PATCH 016/290] Espresso HotSwap plugin API updates that allow callbacks to fire on various redefinition changes --- .../espresso/hotswap/EspressoHotSwap.java | 4 +-- .../espresso/hotswap/HotSwapAction.java | 2 +- .../espresso/hotswap/HotSwapHandler.java | 29 +++++++++++++++---- .../espresso/hotswap/HotSwapPlugin.java | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java index 7ddde00f2b0b..acc24b78cbc0 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -69,9 +69,9 @@ public static void registerHotSwapAction(Class klass, HotSwapAction action) { } } - public static void registerClassInitHotSwap(Class klass, boolean onChange) { + public static void registerClassInitHotSwap(Class klass, boolean onChange, HotSwapAction callback) { if (handler != null) { - handler.registerStaticClassInitHotSwap(klass, onChange); + handler.registerStaticClassInitHotSwap(klass, onChange, callback); } } } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java index 1e43bd871e68..1a81db5905af 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java @@ -41,5 +41,5 @@ package com.oracle.truffle.espresso.hotswap; public interface HotSwapAction { - void onHotSwap(); + void fire(); } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java index 1997b2f7fb12..779335abe068 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java @@ -56,6 +56,7 @@ final class HotSwapHandler { private final Map, Set> hotSwapActions = new HashMap<>(); private final List postHotSwapActions = Collections.synchronizedList(new ArrayList<>()); private final Map, Boolean> staticInitializerHotSwap = new HashMap<>(); + private final Map, List> staticReInitCallBacks = new HashMap<>(); private HotSwapHandler() { } @@ -84,12 +85,20 @@ public void registerPostHotSwapAction(HotSwapAction action) { postHotSwapActions.add(action); } - public void registerStaticClassInitHotSwap(Class klass, boolean onChange) { + public void registerStaticClassInitHotSwap(Class klass, boolean onChange, HotSwapAction callback) { if (!staticInitializerHotSwap.containsKey(klass)) { staticInitializerHotSwap.put(klass, onChange); } else if (!onChange) { staticInitializerHotSwap.put(klass, false); } + if (callback != null) { + List reInitCallbacks = staticReInitCallBacks.get(klass); + if (reInitCallbacks == null) { + reInitCallbacks = new ArrayList<>(1); + staticReInitCallBacks.put(klass, reInitCallbacks); + } + reInitCallbacks.add(callback); + } } @SuppressWarnings("unused") @@ -97,27 +106,35 @@ public void postHotSwap(Class[] changedClasses) { // fire all registered HotSwap actions for (Class klass : changedClasses) { Set actions = hotSwapActions.getOrDefault(klass, Collections.emptySet()); - actions.forEach(HotSwapAction::onHotSwap); + actions.forEach(HotSwapAction::fire); } // fire a generic HotSwap plugin listener for (HotSwapPlugin plugin : plugins) { - plugin.postHotSwap(); + plugin.postHotSwap(changedClasses); } // fire all registered post HotSwap actions - postHotSwapActions.forEach(HotSwapAction::onHotSwap); + postHotSwapActions.forEach(HotSwapAction::fire); } @SuppressWarnings("unused") public boolean rerunClassInit(Class klass, boolean changed) { if (staticInitializerHotSwap.containsKey(klass)) { boolean onlyOnChange = staticInitializerHotSwap.get(klass); - return !onlyOnChange || changed; + boolean rerun = !onlyOnChange || changed; + if (rerun) { + staticReInitCallBacks.getOrDefault(klass, Collections.emptyList()).forEach(HotSwapAction::fire); + } + return rerun; } else { // check class hierarchy for (Map.Entry, Boolean> entry : staticInitializerHotSwap.entrySet()) { Class key = entry.getKey(); if (key.isAssignableFrom(klass)) { - return !entry.getValue() || changed; + boolean rerun = !entry.getValue() || changed; + if (rerun) { + staticReInitCallBacks.getOrDefault(key, Collections.emptyList()).forEach(HotSwapAction::fire); + } + return rerun; } } } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java index edfd48170d98..690d5805681a 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java @@ -43,5 +43,5 @@ public interface HotSwapPlugin { String getName(); - void postHotSwap(); + void postHotSwap(Class[] changedClasses); } From 18b5a42031cd3c99849727d09d471aadf77016ea Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 8 Apr 2021 09:36:52 +0200 Subject: [PATCH 017/290] allow external plugins to run class re-init callbacks also when internal plugins decide to rerun --- .../plugins/impl/RedefinitionPluginHandler.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java index 8f271442793e..2a23f2a1cc1f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -130,17 +130,19 @@ public void onClassLoad(ObjectKlass klass) { // listener methods @Override public boolean rerunClinit(ObjectKlass klass, boolean changed) { + boolean rerun = false; // internal plugins for (InternalRedefinitionPlugin plugin : internalPlugins) { if (plugin.reRunClinit(klass, changed)) { - return true; + rerun = true; + break; } } // external plugins if (externalPluginHandler != null) { - return externalPluginHandler.rerunClassInit(klass, changed); + rerun |= externalPluginHandler.rerunClassInit(klass, changed); } - return false; + return rerun; } @Override From b00c0a62d4f9f6b5d4d104324b5ab99b1858e015 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 14 Apr 2021 08:48:42 +0200 Subject: [PATCH 018/290] add Hotswap API for registering listeners for changes to services declared in META-INF/services --- .../espresso/hotswap/EspressoHotSwap.java | 6 + .../espresso/hotswap/HotSwapHandler.java | 5 + .../espresso/hotswap/ServiceWatcher.java | 286 ++++++++++++++++++ 3 files changed, 297 insertions(+) create mode 100644 espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java index acc24b78cbc0..f86f67d74131 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -74,4 +74,10 @@ public static void registerClassInitHotSwap(Class klass, boolean onChange, Ho handler.registerStaticClassInitHotSwap(klass, onChange, callback); } } + + public static void registerMetaInfServicesListener(Class serviceType, ClassLoader loader, HotSwapAction callback) { + if (handler != null) { + handler.registerMetaInfServicesListener(serviceType, loader, callback); + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java index 779335abe068..76fb54cce033 100644 --- a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java @@ -57,6 +57,7 @@ final class HotSwapHandler { private final List postHotSwapActions = Collections.synchronizedList(new ArrayList<>()); private final Map, Boolean> staticInitializerHotSwap = new HashMap<>(); private final Map, List> staticReInitCallBacks = new HashMap<>(); + private final ServiceWatcher serviceWatcher = new ServiceWatcher(); private HotSwapHandler() { } @@ -101,6 +102,10 @@ public void registerStaticClassInitHotSwap(Class klass, boolean onChange, Hot } } + public void registerMetaInfServicesListener(Class service, ClassLoader loader, HotSwapAction callback) { + serviceWatcher.addServiceWatcher(service, loader, callback); + } + @SuppressWarnings("unused") public void postHotSwap(Class[] changedClasses) { // fire all registered HotSwap actions diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java new file mode 100644 index 000000000000..498b02a61900 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.espresso.hotswap; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.TimerTask; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +final class ServiceWatcher { + + private static final String PREFIX = "META-INF/services/"; + + private final Map> services = Collections.synchronizedMap(new HashMap<>(4)); + + private WatcherThread serviceWatcherThread; + private URLWatcher urlWatcher; + + public void addServiceWatcher(Class service, ClassLoader loader, HotSwapAction callback) { + try { + // cache initial service implementations + Set serviceImpl = Collections.synchronizedSet(new HashSet<>()); + + ServiceLoader serviceLoader = ServiceLoader.load(service, loader); + Iterator iterator = serviceLoader.iterator(); + while (iterator.hasNext()) { + try { + Object o = iterator.next(); + serviceImpl.add(o.getClass().getName()); + } catch (ServiceConfigurationError e) { + // ignore services that we're not able to instantiate + } + } + String fullName = PREFIX + service.getName(); + services.put(fullName, serviceImpl); + + // start watcher threads on first registration + if (serviceWatcherThread == null) { + // start watching + serviceWatcherThread = new WatcherThread(); + serviceWatcherThread.start(); + urlWatcher = new URLWatcher(); + urlWatcher.startWatching(); + } + + // pick up the initial URLs for the service class + ArrayList initialURLs = new ArrayList<>(); + Enumeration urls; + if (loader == null) { + urls = ClassLoader.getSystemResources(fullName); + } else { + urls = loader.getResources(fullName); + } + + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + if ("file".equals(url.getProtocol())) { + // parent directory to register watch on + File file = new File(url.getFile()).getParentFile(); + // listen for changes to the service registration file + initialURLs.add(url); + serviceWatcherThread.addWatch(service.getName(), Paths.get(file.toURI()), () -> onServiceChange(callback, serviceLoader, fullName)); + } + } + + // listen for changes to URLs in the resources + urlWatcher.addWatch(new URLServiceState(initialURLs, loader, fullName, (url) -> { + callback.fire(); + // parent directory to register watch on + File file = new File(url.getFile()).getParentFile(); + // listen for changes to the service registration file + try { + serviceWatcherThread.addWatch(service.getName(), Paths.get(file.toURI()), () -> onServiceChange(callback, serviceLoader, fullName)); + } catch (IOException e) { + // perhaps fallback to reloading and fetching from service loader at intervals? + } + return null; + }, callback::fire)); + } catch (IOException e) { + // perhaps fallback to reloading and fetching from service loader at intervals? + return; + } + } + + private synchronized void onServiceChange(HotSwapAction callback, ServiceLoader serviceLoader, String fullName) { + // sanity-check that the file modification has led to actual changes in services + serviceLoader.reload(); + Set currentServiceImpl = services.getOrDefault(fullName, Collections.emptySet()); + Set changedServiceImpl = Collections.synchronizedSet(new HashSet<>(currentServiceImpl.size() + 1)); + Iterator it = serviceLoader.iterator(); + boolean callbackFired = false; + while (it.hasNext()) { + try { + Object o = it.next(); + changedServiceImpl.add(o.getClass().getName()); + if (!currentServiceImpl.contains(o.getClass().getName())) { + // new service implementation detected + if (!callbackFired) { + callback.fire(); + callbackFired = true; + } + } + } catch (ServiceConfigurationError e) { + // services that we're not able to instantiate are irrelevant + } + } + + if (changedServiceImpl.size() != currentServiceImpl.size() && !callbackFired) { + // removed service implementation, fire callback + callback.fire(); + } + // update the cached services + services.put(fullName, changedServiceImpl); + } + + private final class WatcherThread extends Thread { + + private final WatchService watchService; + private final Map watchActions = Collections.synchronizedMap(new HashMap<>()); + + private WatcherThread() throws IOException { + super("hotswap-watcher-1"); + this.setDaemon(true); + this.watchService = FileSystems.getDefault().newWatchService(); + } + + public void addWatch(String serviceName, Path dir, Runnable callback) throws IOException { + dir.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY); + watchActions.put(serviceName, callback); + } + + @Override + public void run() { + WatchKey key; + try { + while ((key = watchService.take()) != null) { + for (WatchEvent event : key.pollEvents()) { + String fileName = event.context().toString(); + if (watchActions.containsKey(fileName)) { + watchActions.get(fileName).run(); + } + } + key.reset(); + } + } catch (InterruptedException e) { + throw new RuntimeException("Espresso HotSwap service watcher thread was interupted!"); + } + } + } + + private final class URLWatcher { + + private final List cache = Collections.synchronizedList(new ArrayList<>()); + + public void addWatch(URLServiceState state) { + synchronized (cache) { + cache.add(state); + } + } + + public void startWatching() { + TimerTask task = new TimerTask() { + @Override + public void run() { + synchronized (cache) { + for (URLServiceState urlServiceState : cache) { + urlServiceState.detectChanges(); + } + } + } + }; + ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(); + es.scheduleWithFixedDelay(task, 500, 500, TimeUnit.MILLISECONDS); + } + } + + private final class URLServiceState { + private List knownURLs; + private final ClassLoader classLoader; + private final String fullName; + private final Function onAddedURL; + private final Runnable onRemovedURL; + + private URLServiceState(ArrayList initialURLs, ClassLoader classLoader, String fullName, Function onAddedURL, Runnable onRemovedURL) { + this.knownURLs = Collections.synchronizedList(initialURLs); + this.classLoader = classLoader; + this.fullName = fullName; + this.onAddedURL = onAddedURL; + this.onRemovedURL = onRemovedURL; + } + + private void detectChanges() { + try { + boolean changed = false; + ArrayList currentURLs = new ArrayList<>(knownURLs.size()); + Enumeration urls; + if (classLoader == null) { + urls = ClassLoader.getSystemResources(fullName); + } else { + urls = classLoader.getResources(fullName); + } + + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + if ("file".equals(url.getProtocol())) { + currentURLs.add(url); + if (!knownURLs.contains(url)) { + changed = true; + onAddedURL.apply(url); + } + } + } + if (currentURLs.size() != knownURLs.size()) { + changed = true; + onRemovedURL.run(); + } + if (changed) { + // update cache + knownURLs = currentURLs; + } + } catch (IOException e) { + return; + } + } + } +} From 9d324d99c2f247df15e2485bc96af7052e139813 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 14 Apr 2021 08:53:36 +0200 Subject: [PATCH 019/290] logging fixes --- .../oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java | 1 - .../src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java | 2 -- .../com/oracle/truffle/espresso/runtime/JDWPContextImpl.java | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java index 797adcfa36eb..ec3afdffa2b6 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java @@ -602,7 +602,6 @@ private void processPacket(Packet packet) { JDWPLogger.throwing(JDWPLogger.LogLevel.ALL, t); PacketStream reply = new PacketStream().replyPacket().id(packet.id); reply.errorCode(ErrorCodes.INTERNAL); - t.printStackTrace(); handleReply(packet, new CommandResult(reply)); } finally { if (entered) { 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 c8dcb8512a2a..720ab4b05d9d 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 @@ -2990,8 +2990,6 @@ private static void writeMethodResult(PacketStream reply, JDWPContext context, T JDWPLogger.log("Internal Espresso error: %s", JDWPLogger.LogLevel.ALL, t); JDWPLogger.throwing(JDWPLogger.LogLevel.ALL, t); reply.errorCode(ErrorCodes.INTERNAL); - System.out.println("ERROR!"); - t.printStackTrace(); } } 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 7d43e6bb8e1a..5ffdd3ed2294 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 @@ -719,7 +719,7 @@ public void run() { // while rerunning class initializers // in the guest, some anomalies are to // be expected. Treat those as non-fatal - // TODO - add logging + JDWPLogger.log("Exception caught while re-running ", JDWPLogger.LogLevel.REDEFINE); } } } From ba8289926b583ad7753b025032de7ac526c9e7c0 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 14 Apr 2021 08:55:56 +0200 Subject: [PATCH 020/290] add Truffle boundary to onKlassDefined --- .../src/com/oracle/truffle/espresso/impl/ClassRegistries.java | 1 + 1 file changed, 1 insertion(+) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java index a31ee895f5cb..24b6bbfe0b21 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java @@ -305,6 +305,7 @@ public void registerListener(ClassLoadListener listener) { this.classLoadListener = listener; } + @TruffleBoundary public void onKlassDefined(ObjectKlass klass) { if (classLoadListener != null) { classLoadListener.onClassLoad(klass); From db6fb2316d1b856168bbfbfea90a522264651702 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 14 Apr 2021 12:53:23 +0200 Subject: [PATCH 021/290] only start reloader thread if ever needed and enter/leave the Truffle context due to running guest code on the thread --- .../espresso/jdwp/api/JDWPContext.java | 15 +++++ .../jdwp/impl/DebuggerController.java | 1 + .../espresso/runtime/JDWPContextImpl.java | 63 ++++++++++--------- 3 files changed, 51 insertions(+), 28 deletions(-) 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 c5c1fef01064..8d49f5b44955 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 @@ -25,6 +25,7 @@ import java.nio.file.Path; import java.util.List; +import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.nodes.Node; @@ -491,5 +492,19 @@ public interface JDWPContext { */ Node getInstrumentableNode(Node node); + /** + * Determines if hostThread is a VM internal thread. + * + * @param hostThread the thread + * @return true if hostThread is a VM internal thread + */ boolean isSystemThread(Thread hostThread); + + /** + * Sets the Truffle context. + * + * @param con the Truffle context. + */ + void setTruffleContext(TruffleContext con); + } 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 db3b48ce1c89..c96f6127aec8 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 @@ -534,6 +534,7 @@ public void leaveTruffleContext() { @Override public void onLanguageContextInitialized(TruffleContext con, @SuppressWarnings("unused") LanguageInfo language) { truffleContext = con; + context.setTruffleContext(con); } public void suspend(CallFrame currentFrame, Object thread, byte suspendPolicy, List> jobs, SteppingInfo steppingInfo, boolean breakpointHit) { 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 5ffdd3ed2294..b97fad7f0299 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 @@ -33,6 +33,7 @@ import java.util.concurrent.BlockingQueue; import java.util.regex.Matcher; +import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.frame.Frame; @@ -83,6 +84,7 @@ public final class JDWPContextImpl implements JDWPContext { private static final InteropLibrary UNCACHED = InteropLibrary.getUncached(); private final EspressoContext context; + private TruffleContext truffleContext; private final Ids ids; private JDWPSetup setup; private VMListener eventListener = new EmptyListener(); @@ -99,7 +101,6 @@ public JDWPContextImpl(EspressoContext context) { this.innerClassRedefiner = new InnerClassRedefiner(context); this.redefinitionPluginHandler = RedefinitionPluginHandler.create(context); this.classRedefinition = new ClassRedefinition(context, ids, redefinitionPluginHandler); - initializeReloaderThread(); } public VMListener jdwpInit(TruffleLanguage.Env env, Object mainThread) { @@ -706,22 +707,33 @@ public boolean isSystemThread(Thread hostThread) { return hostThread == reloaderThread; } + @Override + public void setTruffleContext(TruffleContext con) { + this.truffleContext = con; + } + private void initializeReloaderThread() { reloaderThread = new Thread(new Runnable() { @Override public void run() { - while (!reloaderThread.isInterrupted()) { - try { - queue.take().fire(); - } catch (InterruptedException e) { - reloaderThread.interrupt(); - } catch (Throwable t) { - // while rerunning class initializers - // in the guest, some anomalies are to - // be expected. Treat those as non-fatal - JDWPLogger.log("Exception caught while re-running ", JDWPLogger.LogLevel.REDEFINE); - } - } + Object prev = null; + try { + prev = truffleContext.enter(null); + while (!reloaderThread.isInterrupted()) { + try { + queue.take().fire(); + } catch (InterruptedException e) { + reloaderThread.interrupt(); + } catch (Throwable t) { + // while rerunning class initializers + // in the guest, some anomalies are to + // be expected. Treat those as non-fatal + JDWPLogger.log("Exception caught while re-running ", JDWPLogger.LogLevel.REDEFINE); + } + } + } finally { + truffleContext.leave(null, prev); + } } }, "reloader"); reloaderThread.start(); @@ -731,25 +743,17 @@ public void rerunclinit(ObjectKlass oldKlass) { queue.add(new ReloadingAction(oldKlass)); } - public void ensureReloadingDone() { - while (!queue.isEmpty()) { - // poll at intervals - try { - Thread.sleep(10); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - @Override public synchronized int redefineClasses(List redefineInfos) { + if (reloaderThread == null) { + initializeReloaderThread(); + } + List changedKlasses = new ArrayList<>(redefineInfos.size()); try { // begin redefine transaction ClassRedefinition.begin(); // list to collect all changed classes - List changedKlasses = new ArrayList<>(redefineInfos.size()); // redefine classes based on direct code changes first doRedefine(redefineInfos, changedKlasses); @@ -760,14 +764,17 @@ public synchronized int redefineClasses(List redefineInfos) { classRedefinition.addExtraReloadClasses(redefineInfos, additional); // redefine additional classes now doRedefine(additional, changedKlasses); - - // run post redefinition plugins - classRedefinition.runPostRedefintionListeners(changedKlasses.toArray(new ObjectKlass[changedKlasses.size()])); } catch (RedefintionNotSupportedException ex) { return ex.getErrorCode(); } finally { ClassRedefinition.end(); } + // run post redefinition plugins + try { + classRedefinition.runPostRedefintionListeners(changedKlasses.toArray(new ObjectKlass[changedKlasses.size()])); + } catch (Throwable t) { + JDWPLogger.throwing(JDWPLogger.LogLevel.REDEFINE, t); + } return 0; } From 0e87fb2bfde02d9803f04e15701951465315ada4 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 14 Apr 2021 14:59:57 +0200 Subject: [PATCH 022/290] Only remove method hooks if it matches the requested input. Make handling of method hooks thread-safe by synchronizing methods and only letting a copied array escape --- .../oracle/truffle/espresso/impl/Method.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) 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 116f1154cbeb..62250f05fb8f 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 @@ -1005,11 +1005,11 @@ public boolean hasActiveHook() { return hasActiveHook.get(); } - public MethodHook[] getMethodHooks() { - return hooks; + public synchronized MethodHook[] getMethodHooks() { + return Arrays.copyOf(hooks, hooks.length); } - public void addMethodHook(MethodHook info) { + public synchronized void addMethodHook(MethodHook info) { hasActiveHook.set(true); if (hooks.length == 0) { hooks = new MethodHook[]{info}; @@ -1020,13 +1020,16 @@ public void addMethodHook(MethodHook info) { hooks[hooks.length - 1] = info; } - public void removeActiveHook(int requestId) { + public synchronized void removeActiveHook(int requestId) { // shrink the array to avoid null values if (hooks.length == 0) { throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); } else if (hooks.length == 1) { - hooks = new MethodHook[0]; - hasActiveHook.set(false); + // make sure it's the right hook + if (hooks[0].getRequestId() == requestId) { + hooks = new MethodHook[0]; + hasActiveHook.set(false); + } } else { int removeIndex = -1; for (int i = 0; i < hooks.length; i++) { @@ -1043,13 +1046,16 @@ public void removeActiveHook(int requestId) { } } - public void removeActiveHook(MethodHook hook) { + public synchronized void removeActiveHook(MethodHook hook) { // shrink the array to avoid null values if (hooks.length == 0) { throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); } else if (hooks.length == 1) { - hooks = new MethodHook[0]; - hasActiveHook.set(false); + // make sure it's the right hook + if (hooks[0] == hook) { + hooks = new MethodHook[0]; + hasActiveHook.set(false); + } } else { int removeIndex = -1; for (int i = 0; i < hooks.length; i++) { From b63747128a7976b26f3253b91eb2468f0ff3fbaa Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 19 Apr 2021 14:03:03 +0200 Subject: [PATCH 023/290] move new HotSwap API to separate project with own distribution --- espresso/mx.espresso/suite.py | 22 ++++++++++++ .../src/LICENSE_HOTSWAP | 35 +++++++++++++++++++ .../espresso/hotswap/EspressoHotSwap.java | 0 .../espresso/hotswap/HotSwapAction.java | 0 .../espresso/hotswap/HotSwapHandler.java | 0 .../espresso/hotswap/HotSwapPlugin.java | 0 .../espresso/hotswap/ServiceWatcher.java | 0 .../espresso/runtime/EspressoProperties.java | 11 +++++- ...uffle_espresso_hotswap_HotSwapHandler.java | 11 ++++-- 9 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.hotswap/src/LICENSE_HOTSWAP rename espresso/src/{com.oracle.truffle.espresso.polyglot => com.oracle.truffle.espresso.hotswap}/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java (100%) rename espresso/src/{com.oracle.truffle.espresso.polyglot => com.oracle.truffle.espresso.hotswap}/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java (100%) rename espresso/src/{com.oracle.truffle.espresso.polyglot => com.oracle.truffle.espresso.hotswap}/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java (100%) rename espresso/src/{com.oracle.truffle.espresso.polyglot => com.oracle.truffle.espresso.hotswap}/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java (100%) rename espresso/src/{com.oracle.truffle.espresso.polyglot => com.oracle.truffle.espresso.hotswap}/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java (100%) diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index 5fefe391e000..0ba2b5126ae3 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -81,6 +81,17 @@ "license": "UPL", }, + "com.oracle.truffle.espresso.hotswap": { + "subDir": "src", + "sourceDirs": ["src"], + "dependencies": [ + ], + "javaCompliance": "1.8+", + "checkstyle": "com.oracle.truffle.espresso.hotswap", + "checkstyleVersion": "8.8", + "license": "UPL", + }, + "com.oracle.truffle.espresso": { "subDir": "src", "sourceDirs": ["src"], @@ -314,6 +325,7 @@ "dependency:espresso:com.oracle.truffle.espresso.eden/", "dependency:espresso:com.oracle.truffle.espresso.native/", "dependency:espresso:POLYGLOT/*", + "dependency:espresso:HOTSWAP/*", ], }, }, @@ -339,6 +351,16 @@ "javadocType": "api", }, + "HOTSWAP": { + "subDir": "src", + "dependencies": [ + "com.oracle.truffle.espresso.hotswap" + ], + "description": "Espresso HotSwap API", + "license": "UPL", + "javadocType": "api", + }, + "DACAPO_SCALA_WARMUP": { "subDir": "src", "dependencies": [ diff --git a/espresso/src/com.oracle.truffle.espresso.hotswap/src/LICENSE_HOTSWAP b/espresso/src/com.oracle.truffle.espresso.hotswap/src/LICENSE_HOTSWAP new file mode 100644 index 000000000000..d656a31cb15d --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.hotswap/src/LICENSE_HOTSWAP @@ -0,0 +1,35 @@ +The Universal Permissive License (UPL), Version 1.0 + +Subject to the condition set forth below, permission is hereby granted to any +person obtaining a copy of this software, associated documentation and/or +data (collectively the "Software"), free of charge and under any and all +copyright rights in the Software, and any and all patent rights owned or +freely licensable by each licensor hereunder covering either (i) the +unmodified Software as contributed to or provided by such licensor, or (ii) +the Larger Works (as defined below), to deal in both + +(a) the Software, and + +(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +one is included with the Software each a "Larger Work" to which the Software +is contributed by such licensors), + +without restriction, including without limitation the rights to copy, create +derivative works of, display, perform, and distribute the Software and make, +use, sell, offer for sale, import, export, have made, and have sold the +Software and the Larger Work(s), and to sublicense the foregoing rights on +either these or other terms. + +This license is subject to the following condition: + +The above copyright notice and either this complete permission notice or at a +minimum a reference to the UPL must be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java similarity index 100% rename from espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java rename to espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java similarity index 100% rename from espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java rename to espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java similarity index 100% rename from espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java rename to espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java similarity index 100% rename from espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java rename to espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java diff --git a/espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java similarity index 100% rename from espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java rename to espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java index aba1a6cf6353..aed3ac5642ea 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java @@ -272,9 +272,18 @@ static Builder processOptions(Builder builder, OptionValues options) { bootClasspath = new ArrayList<>(options.get(EspressoOptions.BootClasspath)); } + // Inject hotswap.jar. + Path espressoHome = HomeFinder.getInstance().getLanguageHomes().get(EspressoLanguage.ID); + Path hotswapJar = espressoHome.resolve("lib").resolve("hotswap.jar"); + if (Files.isReadable(hotswapJar)) { + TruffleLogger.getLogger(EspressoLanguage.ID).fine("Adding HotSwap API to the boot classpath: " + hotswapJar); + bootClasspath.add(hotswapJar); + } else { + TruffleLogger.getLogger(EspressoLanguage.ID).warning("hotswap.jar (HotSwap API) not found at " + espressoHome.resolve("lib")); + } + // Inject polyglot.jar. if (options.get(EspressoOptions.Polyglot)) { - Path espressoHome = HomeFinder.getInstance().getLanguageHomes().get(EspressoLanguage.ID); Path polyglotJar = espressoHome.resolve("lib").resolve("polyglot.jar"); if (Files.isReadable(polyglotJar)) { TruffleLogger.getLogger(EspressoLanguage.ID).fine("Adding Polyglot API to the boot classpath: " + polyglotJar); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java index ed9d17b8b7a6..b0a8faba2141 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java @@ -22,16 +22,23 @@ */ package com.oracle.truffle.espresso.substitutions; +import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.StaticObject; @EspressoSubstitutions public final class Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler { @Substitution - public static boolean registerHandler(@Host(Object.class) StaticObject handler) { + public static boolean registerHandler(@Host(Object.class) StaticObject handler, @InjectMeta Meta meta) { assert handler != null; + + if (meta.getContext().JDWPOptions == null) { + // only allow HotSwap handler registration when running in debug mode + return false; + } + try { - handler.getKlass().getContext().getJdwpContext().registerExternalHotSwapHandler(handler); + meta.getContext().getJdwpContext().registerExternalHotSwapHandler(handler); } catch (IllegalArgumentException ex) { return false; } From abb72754d86907e666fb9382f2a827c38c23e3f0 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 19 Apr 2021 14:12:49 +0200 Subject: [PATCH 024/290] setup checkstyle for new module, same as for the polyglot module --- espresso/mx.espresso/suite.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index 0ba2b5126ae3..0380c2b66eb6 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -87,8 +87,7 @@ "dependencies": [ ], "javaCompliance": "1.8+", - "checkstyle": "com.oracle.truffle.espresso.hotswap", - "checkstyleVersion": "8.8", + "checkstyle": "com.oracle.truffle.espresso.polyglot", "license": "UPL", }, From 54510c2d7d724b41145dc82a315965b15e94f24f Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 19 Apr 2021 14:16:11 +0200 Subject: [PATCH 025/290] remove unused class and add missing header --- .../redefinition/ClassLoadListener.java | 22 +++++++++++++++++++ .../plugins/api/RedefinitionAction.java | 7 ------ 2 files changed, 22 insertions(+), 7 deletions(-) delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java index c22bbbcff6d9..4523cbc58c38 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java @@ -1,3 +1,25 @@ +/* + * Copyright (c) 2021, 2021, 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.redefinition; import com.oracle.truffle.espresso.impl.ObjectKlass; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java deleted file mode 100644 index f084ffc291c1..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefinitionAction.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.oracle.truffle.espresso.redefinition.plugins.api; - -import java.util.Set; - -public interface RedefinitionAction { - Set onChange(); -} From ee64322fad18916beecdfeb168f4cc1633110bc6 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 19 Apr 2021 15:16:09 +0200 Subject: [PATCH 026/290] a few findbugs fixes --- .../plugins/api/RedefineObject.java | 3 --- .../plugins/impl/CachedRedefineObject.java | 23 +++++++++++++------ .../plugins/impl/RedefineObjectImpl.java | 12 +++++----- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java index 46ea8cb42a33..66b7f30055a3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java @@ -25,8 +25,6 @@ import com.oracle.truffle.espresso.jdwp.api.KlassRef; public interface RedefineObject { - boolean notNull(); - KlassRef getKlass(); RedefineObject fromType(String className); @@ -38,5 +36,4 @@ public interface RedefineObject { RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; Object getRawValue(); - } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java index 52bee0103d4a..9399b5fb7bc8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java @@ -54,9 +54,18 @@ public CachedRedefineObject(KlassRef klass) { @Override @TruffleBoundary public Object invoke(String name, RedefineObject... args) throws NoSuchMethodException { + StaticObject theInstance = (StaticObject) getRawValue(); + if (theInstance == null) { + throw new IllegalStateException("cannot invoke method on garbage collection instance"); + } + StringBuilder stringBuffer = new StringBuilder(name); for (RedefineObject arg : args) { - stringBuffer.append(((RedefineObjectImpl) arg).instance.get().getKlass().getNameAsString()); + StaticObject rawArg = (StaticObject) arg.getRawValue(); + if (rawArg == null) { + throw new IllegalStateException("cannot invoke method on garbage collection instance"); + } + stringBuffer.append(rawArg.getKlass().getNameAsString()); } String mapKey = stringBuffer.toString(); Method method = methodsCache.get(mapKey); @@ -67,10 +76,7 @@ public Object invoke(String name, RedefineObject... args) throws NoSuchMethodExc } } if (method != null) { - StaticObject theInstance = instance.get(); - if (theInstance == null) { - throw new IllegalStateException("cannot invoke method on garbage collection instance"); - } + RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; for (int i = 0; i < args.length; i++) { internalArgs[i] = (RedefineObjectImpl) args[i]; @@ -82,11 +88,14 @@ public Object invoke(String name, RedefineObject... args) throws NoSuchMethodExc @Override public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException, IllegalStateException { - // fetch the known declaring class of the method - StaticObject theInstance = instance.get(); if (instance == null) { throw new IllegalStateException(); } + StaticObject theInstance = instance.get(); + if (theInstance == null) { + throw new IllegalStateException(); + } + // fetch the known declaring class of the method Symbol type = context.getTypes().fromClassGetName(className); Klass klassRef = context.getRegistries().findLoadedClass(type, klass.getDefiningClassLoader()); if (klassRef != null) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java index efb9ddf1b114..b85a6de7d2bb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java @@ -172,14 +172,14 @@ protected Object[] rawObjects(RedefineObjectImpl[] args) { return result; } - public boolean notNull() { - return instance != null && instance.get() != StaticObject.NULL; - } - public RedefineObject fromType(String className) { - EspressoContext context = instance.get().getKlass().getContext(); + StaticObject theInstance = (StaticObject) getRawValue(); + if (theInstance == null || theInstance.getKlass() == null) { + return null; + } + EspressoContext context = theInstance.getKlass().getContext(); Symbol type = context.getTypes().fromClassGetName(className); - Klass loadedClass = context.getRegistries().findLoadedClass(type, instance.get().getKlass().getDefiningClassLoader()); + Klass loadedClass = context.getRegistries().findLoadedClass(type, theInstance.getKlass().getDefiningClassLoader()); if (loadedClass != null) { return new CachedRedefineObject(loadedClass.mirror()); } From 7c03239e34cae47b51095668df0afa0837c9b473 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 20 Apr 2021 07:41:06 +0200 Subject: [PATCH 027/290] format pass --- .../espresso/hotswap/EspressoHotSwap.java | 2 +- .../espresso/jdwp/api/JDWPContext.java | 4 +-- .../truffle/espresso/jdwp/api/MethodRef.java | 8 ++++-- .../redefinition/ClassRedefinition.java | 3 ++- .../redefinition/InnerClassRedefiner.java | 27 ++++++++++--------- .../plugins/impl/ExternalPluginHandler.java | 2 +- 6 files changed, 26 insertions(+), 20 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java index f86f67d74131..478f75eb1d1f 100644 --- a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java +++ b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -53,7 +53,7 @@ public static void registerPlugin(HotSwapPlugin plugin) { if (handler != null) { handler.addPlugin(plugin); } else { - // TODO - should we log that plugin registration is only available on supported Espresso VM? + // should we log that plugin registration is only available on supported Espresso VM? } } 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 3d6fbf360a3a..098d0447a79f 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 @@ -493,7 +493,6 @@ public interface JDWPContext { Node getInstrumentableNode(Node node); /** -<<<<<<< HEAD * Determines if hostThread is a VM internal thread. * * @param hostThread the thread @@ -508,7 +507,8 @@ public interface JDWPContext { */ void setTruffleContext(TruffleContext con); - /** Returns the current BCI of the node. + /** + * Returns the current BCI of the node. * * @param rawNode the current node * @param frame the current frame 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 d83c3b4e7671..8e5239103bfe 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 @@ -189,13 +189,17 @@ public interface MethodRef { void addMethodHook(MethodHook info); /** - * Remove a method breakpoint with the given info on this method. + * Remove a method hook with the given info on this method. * * @param requestId the ID for the request that set the breakpoint */ void removedMethodHook(int requestId); - + /** + * Remove a method hook with the given hook on this method. + * + * @param hook the method hook + */ void removedMethodHook(MethodHook hook); /** diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index be5339548170..e2c04608579f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -298,7 +298,8 @@ private static boolean isRemoveMethodSupported() { // detect all types of class changes, but return early when a change that require arbitrary // changes - private static ClassChange detectClassChanges(ParserKlass newParserKlass, ObjectKlass oldKlass, DetectedChange collectedChanges, ParserKlass finalParserKlass) throws RedefintionNotSupportedException { + private static ClassChange detectClassChanges(ParserKlass newParserKlass, ObjectKlass oldKlass, DetectedChange collectedChanges, ParserKlass finalParserKlass) + throws RedefintionNotSupportedException { ClassChange result = ClassChange.NO_CHANGE; ParserKlass oldParserKlass = oldKlass.getLinkedKlass().getParserKlass(); boolean isPatched = finalParserKlass != null; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java index db2bb2e0f7ca..f05ea1cc5eb8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java @@ -22,18 +22,6 @@ */ package com.oracle.truffle.espresso.redefinition; -import com.oracle.truffle.espresso.classfile.ClassfileParser; -import com.oracle.truffle.espresso.descriptors.Symbol; -import com.oracle.truffle.espresso.impl.ClassRegistry; -import com.oracle.truffle.espresso.impl.ConstantPoolPatcher; -import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.ObjectKlass; -import com.oracle.truffle.espresso.jdwp.api.ErrorCodes; -import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; -import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; -import com.oracle.truffle.espresso.runtime.EspressoContext; -import com.oracle.truffle.espresso.runtime.StaticObject; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.instrument.IllegalClassFormatException; @@ -49,6 +37,18 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.oracle.truffle.espresso.classfile.ClassfileParser; +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.impl.ClassRegistry; +import com.oracle.truffle.espresso.impl.ConstantPoolPatcher; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.jdwp.api.ErrorCodes; +import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; +import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; + public final class InnerClassRedefiner { public static final Pattern ANON_INNER_CLASS_PATTERN = Pattern.compile(".*\\$\\d+.*"); @@ -218,7 +218,8 @@ private Symbol getOuterClassName(Symbol innerName) { return context.getNames().getOrCreate(strName.substring(0, strName.lastIndexOf('$'))); } - private void matchClassInfo(HotSwapClassInfo hotSwapInfo, List removedInnerClasses, Map, Symbol>> renamingRules) throws RedefintionNotSupportedException { + private void matchClassInfo(HotSwapClassInfo hotSwapInfo, List removedInnerClasses, Map, Symbol>> renamingRules) + throws RedefintionNotSupportedException { Klass klass = hotSwapInfo.getKlass(); // try to fetch all direct inner classes // based on the constant pool in the class bytes diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java index 16158bec18fa..8e810758cbcf 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java @@ -46,7 +46,7 @@ public static ExternalPluginHandler create(StaticObject guestHandler) throws Ill INTEROP = InteropLibrary.getFactory().create(guestHandler); boolean invocable = INTEROP.isMemberInvocable(guestHandler, RERUN_CLINIT) && - INTEROP.isMemberInvocable(guestHandler, POST_HOTSWAP); + INTEROP.isMemberInvocable(guestHandler, POST_HOTSWAP); if (!invocable) { throw new IllegalArgumentException("guest handler does not implement expected API"); From 0de71aa0d12a262fc34af7729799648343761f56 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 20 Apr 2021 10:40:23 +0200 Subject: [PATCH 028/290] always rerun static initializer for changed JDK proxy classes --- .../plugins/jdkproxy/JDKProxyRedefinitionPlugin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index 1e4eed94d824..ae9133ee33ab 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -146,9 +146,9 @@ public synchronized void fillExtraReloadClasses(List redefineInfos @Override public boolean reRunClinit(KlassRef klass, boolean changed) { - // changed Dynamic Proxy classes has cached Method references - // in static fields, so re-run the static initializer - return changed && proxySuperKlass.isAssignable(klass); + // changed Dynamic Proxy classes have cached Method references + // in static fields, so always re-run the static initializer + return proxySuperKlass.isAssignable(klass); } private final class ProxyCache { From 84c174a454d4faa368f7bccac5b6303521ec4dac Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 20 Apr 2021 11:21:17 +0200 Subject: [PATCH 029/290] disable JDK Dynamic Proxy integration until changes to fields are supported --- ....espresso.redefinition.plugins.api.InternalRedefinitionPlugin | 1 - 1 file changed, 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin index 46bb33322bc8..f5619c4e63d6 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin +++ b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin @@ -1,2 +1 @@ -com.oracle.truffle.espresso.redefinition.plugins.jdkproxy.JDKProxyRedefinitionPlugin com.oracle.truffle.espresso.redefinition.plugins.jdkcaches.JDKCacheRedefinitionPlugin From b1d6960689da3040640156201fad01a0aea4bfef Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 20 Apr 2021 14:20:23 +0200 Subject: [PATCH 030/290] throw when we can't find a method hook --- .../src/com/oracle/truffle/espresso/impl/Method.java | 3 +++ 1 file changed, 3 insertions(+) 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 ba801d2dc388..ccd0f92d92ea 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 @@ -1044,6 +1044,9 @@ public synchronized void removeActiveHook(int requestId) { break; } } + if (removeIndex == -1) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); + } MethodHook[] temp = new MethodHook[hooks.length - 1]; for (int i = 0; i < temp.length; i++) { temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; From 84343bb6f57b6b3de552f6f0da6a1c93844ee556 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 20 Apr 2021 14:21:10 +0200 Subject: [PATCH 031/290] use Symbol instead of String in internal HotSwap API --- .../truffle/espresso/descriptors/Symbol.java | 11 +++++++++++ .../api/InternalRedefinitionPlugin.java | 17 ++++++++--------- .../plugins/api/MethodLocator.java | 12 +++++++----- .../redefinition/plugins/api/TriggerClass.java | 11 ++++++----- .../impl/RedefinitionPluginHandler.java | 2 +- .../jdkcaches/JDKCacheRedefinitionPlugin.java | 18 +++++++++--------- .../jdkproxy/JDKProxyRedefinitionPlugin.java | 11 +++++------ 7 files changed, 47 insertions(+), 35 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java index ea1f24a82826..9cbc016d9d13 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java @@ -502,6 +502,10 @@ public static void ensureInitialized() { public static final Symbol PARSE_ERROR = StaticSymbols.putName("PARSE_ERROR"); public static final Symbol create = StaticSymbols.putName("create"); + // Class redefinition plugin helpers + public static final Symbol flushFromCaches = StaticSymbols.putName("flushFromCaches"); + public static final Symbol generateProxyClass = StaticSymbols.putName("generateProxyClass"); + } /** @@ -551,6 +555,12 @@ public static void ensureInitialized() { public static final Symbol jdk_internal_loader_ClassLoaders$PlatformClassLoader = StaticSymbols.putType("Ljdk/internal/loader/ClassLoaders$PlatformClassLoader;"); + public static final Symbol java_beans_Introspector = StaticSymbols.putType("Ljava/beans/Introspector;"); + public static final Symbol java_beans_ThreadGroupContext = StaticSymbols.putType("Ljava/beans/ThreadGroupContext;"); + + public static final Symbol java_lang_reflect_Proxy = StaticSymbols.putType("Ljava/lang/reflect/Proxy;"); + public static final Symbol sun_misc_ProxyGenerator = StaticSymbols.putType("Lsun/misc/ProxyGenerator;"); + // Primitive types. public static final Symbol _boolean = StaticSymbols.putType("Z" /* boolean */); public static final Symbol _byte = StaticSymbols.putType("B" /* byte */); @@ -847,6 +857,7 @@ public static void ensureInitialized() { public static final Symbol Object_long_int_int_int_int = StaticSymbols.putSignature(Type.java_lang_Object, Type._long, Type._int, Type._int, Type._int, Type._int); public static final Symbol Object_long_int_int_int_Object_array = StaticSymbols.putSignature(Type.java_lang_Object, Type._long, Type._int, Type._int, Type._int, Type.java_lang_Object_array); + public static final Symbol _byte_array_String_Class_array_int = StaticSymbols.putSignature(Type._byte_array, Type.java_lang_String, Type.java_lang_Class_array, Type._int); public static final Symbol Boolean_boolean = StaticSymbols.putSignature(Type.java_lang_Boolean, Type._boolean); public static final Symbol Byte_byte = StaticSymbols.putSignature(Type.java_lang_Byte, Type._byte); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java index 7402f0ce2b6d..44dfa83b56cf 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java @@ -26,10 +26,11 @@ import java.util.List; import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Method; 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.RedefineInfo; import com.oracle.truffle.espresso.redefinition.plugins.impl.CachedRedefineObject; import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefinitionPluginHandler; @@ -42,8 +43,6 @@ public abstract class InternalRedefinitionPlugin { protected static final InteropLibrary INTEROP = InteropLibrary.getUncached(); - private static final String constructorName = ""; - private EspressoContext context; private RedefinitionPluginHandler redefinitionPluginHandler; @@ -97,8 +96,8 @@ protected void registerClassLoadAction(String className, ClassLoadAction action) } protected void hookMethodExit(KlassRef klass, MethodLocator hookSpec, MethodHook.Kind kind, MethodExitHook onExitHook) { - for (MethodRef method : klass.getDeclaredMethodRefs()) { - if (method.getNameAsString().equals(hookSpec.getName()) && method.getSignatureAsString().equals(hookSpec.getSignature())) { + for (Method method : ((Klass) klass).getDeclaredMethods()) { + if (method.getName().equals(hookSpec.getName()) && method.getRawSignature().equals(hookSpec.getSignature())) { method.addMethodHook(new RedefintionHook(onExitHook, kind)); break; } @@ -106,8 +105,8 @@ protected void hookMethodExit(KlassRef klass, MethodLocator hookSpec, MethodHook } protected void hookMethodEntry(KlassRef klass, MethodLocator hookSpec, MethodHook.Kind kind, MethodEntryHook onEntryHook) { - for (MethodRef method : klass.getDeclaredMethodRefs()) { - if (method.getNameAsString().equals(hookSpec.getName()) && method.getSignatureAsString().equals(hookSpec.getSignature())) { + for (Method method : ((Klass) klass).getDeclaredMethods()) { + if (method.getName().equals(hookSpec.getName()) && method.getRawSignature().equals(hookSpec.getSignature())) { method.addMethodHook(new RedefintionHook(onEntryHook, kind)); break; } @@ -115,8 +114,8 @@ protected void hookMethodEntry(KlassRef klass, MethodLocator hookSpec, MethodHoo } protected void hookConstructor(KlassRef klass, MethodHook.Kind kind, MethodEntryHook onEntryHook) { - for (MethodRef method : klass.getDeclaredMethodRefs()) { - if (method.getNameAsString().equals(constructorName)) { + for (Method method : ((Klass) klass).getDeclaredMethods()) { + if (method.getName().equals(Symbol.Name._init_)) { method.addMethodHook(new RedefintionHook(onEntryHook, kind)); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java index 7b2137a35dcd..4afd1139f08d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java @@ -22,20 +22,22 @@ */ package com.oracle.truffle.espresso.redefinition.plugins.api; +import com.oracle.truffle.espresso.descriptors.Symbol; + public final class MethodLocator { - private final String name; - private final String signature; + private final Symbol name; + private final Symbol signature; - public MethodLocator(String name, String signature) { + public MethodLocator(Symbol name, Symbol signature) { this.name = name; this.signature = signature; } - public String getName() { + public Symbol getName() { return name; } - public String getSignature() { + public Symbol getSignature() { return signature; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java index 748ee0b84a0c..bf3a60fe97c1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java @@ -22,22 +22,23 @@ */ package com.oracle.truffle.espresso.redefinition.plugins.api; +import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.jdwp.api.KlassRef; public final class TriggerClass { - private String className; + private Symbol triggerType; private final InternalRedefinitionPlugin plugin; private final TriggerClassHook hook; - public TriggerClass(String className, InternalRedefinitionPlugin plugin, TriggerClassHook hook) { - this.className = className; + public TriggerClass(Symbol triggerType, InternalRedefinitionPlugin plugin, TriggerClassHook hook) { + this.triggerType = triggerType; this.plugin = plugin; this.hook = hook; } - public String getClassName() { - return className; + public Symbol getTriggerType() { + return triggerType; } public InternalRedefinitionPlugin getPlugin() { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java index 2a23f2a1cc1f..0c538b0bd97a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -85,7 +85,7 @@ public static RedefinitionPluginHandler create(EspressoContext espressoContext) while (pluginIterator.hasNext()) { InternalRedefinitionPlugin plugin = pluginIterator.next(); for (TriggerClass triggerClass : plugin.getTriggerClasses()) { - Symbol triggerType = espressoContext.getTypes().fromClassGetName(triggerClass.getClassName()); + Symbol triggerType = triggerClass.getTriggerType(); Set triggerClasses = triggers.get(triggerType); if (triggerClasses == null) { triggerClasses = new HashSet<>(1); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java index ccb4effcce84..575ce7af0d20 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java @@ -23,6 +23,11 @@ package com.oracle.truffle.espresso.redefinition.plugins.jdkcaches; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; @@ -31,18 +36,13 @@ import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - public final class JDKCacheRedefinitionPlugin extends InternalRedefinitionPlugin { - public static final String INTROSPECTOR_CLASS = "java.beans.Introspector"; - public static final String FLUSH_CACHES_METHOD = "flushFromCaches"; - public static final String FLUSH_CACHES_SIG = "(Ljava/lang/Class;)V"; + public static final Symbol INTROSPECTOR_CLASS = Symbol.Type.java_beans_Introspector; + public static final Symbol FLUSH_CACHES_METHOD = Symbol.Name.flushFromCaches; private MethodRef flushFromCachesMethod; - public static final String THREAD_GROUP_CONTEXT = "java.beans.ThreadGroupContext"; + public static final Symbol THREAD_GROUP_CONTEXT = Symbol.Type.java_beans_ThreadGroupContext; public static final String REMOVE_BEAN_INFO = "removeBeanInfo"; private List threadGroupContext = Collections.synchronizedList(new ArrayList<>(1)); @@ -55,7 +55,7 @@ public String getName() { public TriggerClass[] getTriggerClasses() { TriggerClass[] triggerClasses = new TriggerClass[2]; triggerClasses[0] = new TriggerClass(INTROSPECTOR_CLASS, this, klass -> { - hookMethodEntry(klass, new MethodLocator(FLUSH_CACHES_METHOD, FLUSH_CACHES_SIG), MethodHook.Kind.ONE_TIME, + hookMethodEntry(klass, new MethodLocator(FLUSH_CACHES_METHOD, Symbol.Signature._void_Class), MethodHook.Kind.ONE_TIME, ((method, variables) -> flushFromCachesMethod = method)); }); triggerClasses[1] = new TriggerClass(THREAD_GROUP_CONTEXT, this, klass -> { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index ae9133ee33ab..19ebb73d4933 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -31,6 +31,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; @@ -41,12 +42,10 @@ import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; public final class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { + private static final Symbol PROXY_GENERATOR_CLASS = Symbol.Type.sun_misc_ProxyGenerator; + private static final Symbol GENERATOR_METHOD = Symbol.Name.generateProxyClass; - private static final String PROXY_GENERATOR_CLASS = "sun.misc.ProxyGenerator"; - private static final String GENERATOR_METHOD = "generateProxyClass"; - private static final String GENERATOR_METHOD_SIG = "(Ljava/lang/String;[Ljava/lang/Class;I)[B"; - - private static final String PROXY_SUPER_CLASS = "java.lang.reflect.Proxy"; + private static final Symbol PROXY_SUPER_CLASS = Symbol.Type.java_lang_reflect_Proxy; private final Map> cache = Collections.synchronizedMap(new HashMap<>()); @@ -66,7 +65,7 @@ public TriggerClass[] getTriggerClasses() { // trigger on proxy generator class and add generator method hooks triggerClasses[0] = new TriggerClass(PROXY_GENERATOR_CLASS, this, klass -> { // hook into the proxy generator method to obtain proxy generation arguments - hookMethodEntry(klass, new MethodLocator(GENERATOR_METHOD, GENERATOR_METHOD_SIG), MethodHook.Kind.INDEFINITE, (method, variables) -> { + hookMethodEntry(klass, new MethodLocator(GENERATOR_METHOD, Symbol.Signature._byte_array_String_Class_array_int), MethodHook.Kind.INDEFINITE, (method, variables) -> { if (generationInProgress.get()) { // don't hook when we're re-generating proxy bytes return; From 36589d82b213802377d87600e869786efcdb4880 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 21 Apr 2021 09:38:38 +0200 Subject: [PATCH 032/290] add Javadoc for new HotSwap API --- .../espresso/hotswap/EspressoHotSwap.java | 77 ++++++++++++++++--- .../espresso/hotswap/HotSwapAction.java | 11 +++ .../espresso/hotswap/HotSwapPlugin.java | 20 +++++ 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java index 478f75eb1d1f..462a31ee4dfa 100644 --- a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java +++ b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/EspressoHotSwap.java @@ -40,7 +40,14 @@ */ package com.oracle.truffle.espresso.hotswap; -@SuppressWarnings("unused") +/** + * Provides access to the enhanced HotSwap capabilities of Espresso. Every method allows + * registration of HotSwap actions that will be fired on relevant class redefinition changes. + * + * Note: This class will not register anything unless running Java on truffle. + * + * @since 21.2 + */ public final class EspressoHotSwap { private EspressoHotSwap() { @@ -49,35 +56,85 @@ private EspressoHotSwap() { private static final HotSwapHandler handler = HotSwapHandler.create(); - public static void registerPlugin(HotSwapPlugin plugin) { + /** + * Registration of a HotSwap plugin for which all generic actions are fired during HotSwap. One + * example of such action is the {@link HotSwapPlugin#postHotSwap(Class[])} that is fired for a + * registered plugin. + * + * Note: The Plugin API is expected to change to allow plugins to receive finer-grained changes. + * + * @param plugin the plugin to register + * @return true if registration was successful + * @since 21.2 + */ + public static boolean registerPlugin(HotSwapPlugin plugin) { if (handler != null) { handler.addPlugin(plugin); - } else { - // should we log that plugin registration is only available on supported Espresso VM? } + return handler != null; } - public static void registerPostHotSwapAction(HotSwapAction action) { + /** + * Registration of a generic post HotSwap action that will be fired after class redefinition + * completed. + * + * @param action the action to fire + * @return true if registration was successful + * @since 21.2 + */ + public static boolean registerPostHotSwapAction(HotSwapAction action) { if (handler != null) { handler.registerPostHotSwapAction(action); } + return handler != null; } - public static void registerHotSwapAction(Class klass, HotSwapAction action) { + /** + * Registration of a HotSwap action that will be fired in case the {@code klass} changes. + * + * @param action the action to fire + * @return true if registration was successful + * @since 21.2 + */ + public static boolean registerHotSwapAction(Class klass, HotSwapAction action) { if (handler != null) { handler.registerHotSwapAction(klass, action); } + return handler != null; } - public static void registerClassInitHotSwap(Class klass, boolean onChange, HotSwapAction callback) { + /** + * Registration of a HotSwap action that is fired if the {@code klass} or any subclass thereof + * has a changed static initializer. Use {@code onChange} to control if the action should only + * fire when the static initializer actually changed. + * + * @param klass the class instance + * @param onChange if action should be fired only when the static initializer changes + * @param action the action to fire + * @return true if registration was successful + * @since 21.2 + */ + public static boolean registerClassInitHotSwap(Class klass, boolean onChange, HotSwapAction action) { if (handler != null) { - handler.registerStaticClassInitHotSwap(klass, onChange, callback); + handler.registerStaticClassInitHotSwap(klass, onChange, action); } + return handler != null; } - public static void registerMetaInfServicesListener(Class serviceType, ClassLoader loader, HotSwapAction callback) { + /** + * Registration of a HotSwap action that will be fired if changes are detected to the declared + * META-INF/services for {@code serviceType}. + * + * @param serviceType the class instance of the service type + * @param loader the class loader to lookup the service type + * @param action the action to fire + * @return true if registration was successful + * @since 21.2 + */ + public static boolean registerMetaInfServicesListener(Class serviceType, ClassLoader loader, HotSwapAction action) { if (handler != null) { - handler.registerMetaInfServicesListener(serviceType, loader, callback); + handler.registerMetaInfServicesListener(serviceType, loader, action); } + return handler != null; } } diff --git a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java index 1a81db5905af..b5f5845c9804 100644 --- a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java +++ b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapAction.java @@ -40,6 +40,17 @@ */ package com.oracle.truffle.espresso.hotswap; +/** + * Represents all HotSwap actions that have been registered. See {@link EspressoHotSwap}. + * + * @since 21.2 + */ public interface HotSwapAction { + + /** + * Callback method for registered HotSwap actions. + * + * @since 21.2 + */ void fire(); } diff --git a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java index 690d5805681a..756948666290 100644 --- a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapPlugin.java @@ -40,8 +40,28 @@ */ package com.oracle.truffle.espresso.hotswap; +/** + * Represents a generic plugin that can be registered by + * {@link EspressoHotSwap#registerPlugin(HotSwapPlugin)}. + * + * @since 21.2 + */ public interface HotSwapPlugin { + + /** + * The name of the plugin. + * + * @return the plugin name + * @since 21.2 + */ String getName(); + /** + * Callback method called after HotSwapping completed all class redefinitions. Allows a plugin + * implementation to run custom code depending on the changed classes. + * + * @param changedClasses all classes that changed during HotSwap + * @since 21.2 + */ void postHotSwap(Class[] changedClasses); } From 35b145bb180680e9cbcdaeb54187ceb3b86aaf29 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 22 Apr 2021 11:14:50 +0200 Subject: [PATCH 033/290] Refactor internal redefinition API. Use standard Espresso substitutions and create a lookup of redefinition plugins from the context. --- ...ion.plugins.api.InternalRedefinitionPlugin | 1 + .../truffle/espresso/descriptors/Symbol.java | 2 + .../oracle/truffle/espresso/meta/Meta.java | 35 ++++ .../api/InternalRedefinitionPlugin.java | 75 +------ .../plugins/api/MethodEntryHook.java | 30 --- .../plugins/api/MethodExitHook.java | 29 --- .../plugins/api/MethodLocator.java | 43 ---- .../plugins/api/RedefineObject.java | 39 ---- .../plugins/api/RedefintionHook.java | 75 ------- .../plugins/api/TriggerClass.java | 51 ----- .../plugins/api/TriggerClassHook.java | 29 --- .../plugins/impl/CachedRedefineObject.java | 139 ------------- .../plugins/impl/RedefineObjectImpl.java | 188 ------------------ .../impl/RedefinitionPluginHandler.java | 34 +--- .../plugins/impl/UncachedRedefineObject.java | 67 ------- .../jdkcaches/JDKCacheRedefinitionPlugin.java | 63 ++---- .../jdkproxy/JDKProxyRedefinitionPlugin.java | 129 ++++-------- .../espresso/runtime/EspressoContext.java | 20 ++ .../Target_java_beans_ThreadGroupContext.java | 49 +++++ ...rget_java_lang_reflect_ProxyGenerator.java | 52 +++++ .../Target_sun_misc_ProxyGenerator.java | 51 +++++ 21 files changed, 286 insertions(+), 915 deletions(-) delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodEntryHook.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodExitHook.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/MethodLocator.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_beans_ThreadGroupContext.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_ProxyGenerator.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_misc_ProxyGenerator.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin index f5619c4e63d6..ea70a57037f4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin +++ b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin @@ -1 +1,2 @@ com.oracle.truffle.espresso.redefinition.plugins.jdkcaches.JDKCacheRedefinitionPlugin +com.oracle.truffle.espresso.redefinition.plugins.jdkproxy.JDKProxyRedefinitionPlugin diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java index 9cbc016d9d13..98a7d1d5fac4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java @@ -505,6 +505,7 @@ public static void ensureInitialized() { // Class redefinition plugin helpers public static final Symbol flushFromCaches = StaticSymbols.putName("flushFromCaches"); public static final Symbol generateProxyClass = StaticSymbols.putName("generateProxyClass"); + public static final Symbol removeBeanInfo = StaticSymbols.putName("removeBeanInfo"); } @@ -559,6 +560,7 @@ public static void ensureInitialized() { public static final Symbol java_beans_ThreadGroupContext = StaticSymbols.putType("Ljava/beans/ThreadGroupContext;"); public static final Symbol java_lang_reflect_Proxy = StaticSymbols.putType("Ljava/lang/reflect/Proxy;"); + public static final Symbol java_lang_reflect_ProxyGenerator = StaticSymbols.putType("Ljava/lang/reflect/ProxyGenerator;"); public static final Symbol sun_misc_ProxyGenerator = StaticSymbols.putType("Lsun/misc/ProxyGenerator;"); // Primitive types. diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java index 2848c60e87a7..9eb3a14bb885 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java @@ -646,6 +646,29 @@ public Meta(EspressoContext context) { java_util_NoSuchElementException = knownKlass(Type.java_util_NoSuchElementException); interopDispatch = new InteropKlassesDispatch(this); + + // used for class redefinition + java_lang_reflect_Proxy = knownKlass(Type.java_lang_reflect_Proxy); + java_beans_ThreadGroupContext = knownKlass(Type.java_beans_ThreadGroupContext); + java_beans_ThreadGroupContext_init = java_beans_ThreadGroupContext.requireDeclaredMethod(Name._init_, Signature._void); + java_beans_ThreadGroupContext_removeBeanInfo = java_beans_ThreadGroupContext.requireDeclaredMethod(Name.removeBeanInfo, Signature._void_Class); + java_beans_Introspector = knownKlass(Type.java_beans_Introspector); + java_beans_Introspector_flushFromCaches = java_beans_Introspector.requireDeclaredMethod(Name.flushFromCaches, Signature._void_Class); + + // sun.misc.Proxygenerator -> java.lang.reflect.Proxygenerator in JDK 9 + if (getJavaVersion().java8OrEarlier()) { + sun_misc_ProxyGenerator = knownKlass(Type.sun_misc_ProxyGenerator); + sun_misc_ProxyGenerator_generateProxyClass = sun_misc_ProxyGenerator.lookupDeclaredMethod(Name.generateProxyClass, Signature._byte_array_String_Class_array_int); + + java_lang_reflect_ProxyGenerator = null; + java_lang_reflect_ProxyGenerator_generateProxyClass = null; + } else { + sun_misc_ProxyGenerator = null; + sun_misc_ProxyGenerator_generateProxyClass = null; + + java_lang_reflect_ProxyGenerator = knownKlass(Type.java_lang_reflect_ProxyGenerator); + java_lang_reflect_ProxyGenerator_generateProxyClass = java_lang_reflect_ProxyGenerator.requireDeclaredMethod(Name.generateProxyClass, Signature._byte_array_String_Class_array_int); + } } /** @@ -795,6 +818,18 @@ private ObjectKlass loadKlassWithBootClassLoaderDiffVersion(Symbol t1, Sym public final Field java_lang_Double_value; public final Field java_lang_Long_value; + // used by class redefinition + public final ObjectKlass java_lang_reflect_Proxy; + public final ObjectKlass sun_misc_ProxyGenerator; + public final Method sun_misc_ProxyGenerator_generateProxyClass; + public final ObjectKlass java_lang_reflect_ProxyGenerator; + public final Method java_lang_reflect_ProxyGenerator_generateProxyClass; + public final ObjectKlass java_beans_ThreadGroupContext; + public final Method java_beans_ThreadGroupContext_init; + public final Method java_beans_ThreadGroupContext_removeBeanInfo; + public final ObjectKlass java_beans_Introspector; + public final Method java_beans_Introspector_flushFromCaches; + // Guest String. public final Field java_lang_String_value; public final Field java_lang_String_hash; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java index 44dfa83b56cf..e0f4191f8f7b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java @@ -22,27 +22,15 @@ */ package com.oracle.truffle.espresso.redefinition.plugins.api; -import java.util.Collection; import java.util.List; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.espresso.descriptors.Symbol; -import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.jdwp.api.KlassRef; -import com.oracle.truffle.espresso.jdwp.api.MethodHook; +import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; -import com.oracle.truffle.espresso.redefinition.plugins.impl.CachedRedefineObject; import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefinitionPluginHandler; -import com.oracle.truffle.espresso.redefinition.plugins.impl.UncachedRedefineObject; import com.oracle.truffle.espresso.runtime.EspressoContext; -import com.oracle.truffle.espresso.runtime.StaticObject; -import com.oracle.truffle.espresso.substitutions.Host; public abstract class InternalRedefinitionPlugin { - protected static final InteropLibrary INTEROP = InteropLibrary.getUncached(); - private EspressoContext context; private RedefinitionPluginHandler redefinitionPluginHandler; @@ -55,11 +43,7 @@ public void activate(EspressoContext espressoContext, RedefinitionPluginHandler this.redefinitionPluginHandler = handler; } - public abstract String getName(); - - public abstract TriggerClass[] getTriggerClasses(); - - public boolean reRunClinit(@SuppressWarnings("unused") KlassRef klass, @SuppressWarnings("unused") boolean changed) { + public boolean reRunClinit(@SuppressWarnings("unused") ObjectKlass klass, @SuppressWarnings("unused") boolean changed) { return false; } @@ -67,64 +51,11 @@ public void fillExtraReloadClasses(@SuppressWarnings("unused") List name; - private final Symbol signature; - - public MethodLocator(Symbol name, Symbol signature) { - this.name = name; - this.signature = signature; - } - - public Symbol getName() { - return name; - } - - public Symbol getSignature() { - return signature; - } -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java deleted file mode 100644 index 66b7f30055a3..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefineObject.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; - -import com.oracle.truffle.espresso.jdwp.api.KlassRef; - -public interface RedefineObject { - KlassRef getKlass(); - - RedefineObject fromType(String className); - - Object invoke(String name, RedefineObject... args) throws NoSuchMethodException; - - RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException; - - RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; - - Object getRawValue(); -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java deleted file mode 100644 index c69f8d710e50..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/RedefintionHook.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; - -import com.oracle.truffle.espresso.jdwp.api.MethodHook; -import com.oracle.truffle.espresso.jdwp.api.MethodRef; -import com.oracle.truffle.espresso.jdwp.api.MethodVariable; - -public final class RedefintionHook implements MethodHook { - - private final MethodExitHook onExitHook; - private final MethodEntryHook onEntryHook; - private final Kind kind; - private boolean hasFired = false; - - public RedefintionHook(MethodEntryHook onEntryHook, Kind kind) { - this.onExitHook = null; - this.onEntryHook = onEntryHook; - this.kind = kind; - } - - public RedefintionHook(MethodExitHook onExitHook, Kind kind) { - this.onExitHook = onExitHook; - this.onEntryHook = null; - this.kind = kind; - } - - @Override - public Kind getKind() { - return kind; - } - - @Override - public boolean onMethodEnter(MethodRef method, MethodVariable[] variables) { - if (onEntryHook != null) { - onEntryHook.onMethodEnter(method, variables); - hasFired = true; - } - return false; - } - - @Override - public boolean onMethodExit(MethodRef method, Object returnValue) { - if (onExitHook != null) { - onExitHook.onMethodExit(method, returnValue); - hasFired = true; - } - return false; - } - - @Override - public boolean hasFired() { - return hasFired; - } -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java deleted file mode 100644 index bf3a60fe97c1..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClass.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; - -import com.oracle.truffle.espresso.descriptors.Symbol; -import com.oracle.truffle.espresso.jdwp.api.KlassRef; - -public final class TriggerClass { - - private Symbol triggerType; - private final InternalRedefinitionPlugin plugin; - private final TriggerClassHook hook; - - public TriggerClass(Symbol triggerType, InternalRedefinitionPlugin plugin, TriggerClassHook hook) { - this.triggerType = triggerType; - this.plugin = plugin; - this.hook = hook; - } - - public Symbol getTriggerType() { - return triggerType; - } - - public InternalRedefinitionPlugin getPlugin() { - return plugin; - } - - public void fire(KlassRef klass) { - hook.fire(klass); - } -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java deleted file mode 100644 index a945fb986193..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/TriggerClassHook.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.api; - -import com.oracle.truffle.espresso.jdwp.api.KlassRef; - -public interface TriggerClassHook { - void fire(KlassRef klass); -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java deleted file mode 100644 index 9399b5fb7bc8..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/CachedRedefineObject.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; - -import java.util.HashMap; - -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.espresso.descriptors.Symbol; -import com.oracle.truffle.espresso.impl.Field; -import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.jdwp.api.KlassRef; -import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; -import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; -import com.oracle.truffle.espresso.runtime.EspressoContext; -import com.oracle.truffle.espresso.runtime.StaticObject; - -public final class CachedRedefineObject extends RedefineObjectImpl { - - protected final EspressoContext context; - private final HashMap methodsCache = new HashMap<>(1); - private final HashMap fieldsCache = new HashMap<>(1); - - public CachedRedefineObject(StaticObject object) { - super(object); - this.context = object.getKlass().getContext(); - } - - public CachedRedefineObject(KlassRef klass) { - super(klass); - this.context = ((Klass) klass).getContext(); - } - - @Override - @TruffleBoundary - public Object invoke(String name, RedefineObject... args) throws NoSuchMethodException { - StaticObject theInstance = (StaticObject) getRawValue(); - if (theInstance == null) { - throw new IllegalStateException("cannot invoke method on garbage collection instance"); - } - - StringBuilder stringBuffer = new StringBuilder(name); - for (RedefineObject arg : args) { - StaticObject rawArg = (StaticObject) arg.getRawValue(); - if (rawArg == null) { - throw new IllegalStateException("cannot invoke method on garbage collection instance"); - } - stringBuffer.append(rawArg.getKlass().getNameAsString()); - } - String mapKey = stringBuffer.toString(); - Method method = methodsCache.get(mapKey); - if (method == null) { - method = lookupMethod(name, args); - if (method != null) { - methodsCache.put(mapKey, method); - } - } - if (method != null) { - - RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; - for (int i = 0; i < args.length; i++) { - internalArgs[i] = (RedefineObjectImpl) args[i]; - } - return method.invokeDirect(theInstance, rawObjects(internalArgs)); - } - throw new NoSuchMethodException(); - } - - @Override - public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException, IllegalStateException { - if (instance == null) { - throw new IllegalStateException(); - } - StaticObject theInstance = instance.get(); - if (theInstance == null) { - throw new IllegalStateException(); - } - // fetch the known declaring class of the method - Symbol type = context.getTypes().fromClassGetName(className); - Klass klassRef = context.getRegistries().findLoadedClass(type, klass.getDefiningClassLoader()); - if (klassRef != null) { - Method method = lookupMethod(klassRef, methodName, args); - if (method != null) { - RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; - for (int i = 0; i < args.length; i++) { - internalArgs[i] = (RedefineObjectImpl) args[i]; - } - return InternalRedefinitionPlugin.createUncached(method.invokeDirect(theInstance, rawObjects(internalArgs))); - } - } - throw new NoSuchMethodException(); - } - - @Override - public RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException { - StaticObject theInstance = instance.get(); - if (theInstance == null) { - throw new IllegalStateException("cannot get field on garbage collection instance"); - } - Field field = fieldsCache.get(fieldName); - if (field == null) { - Klass klassRef = klass; - while (klassRef != null) { - for (Field declaredField : klassRef.getDeclaredFields()) { - if (declaredField.getNameAsString().equals(fieldName)) { - field = declaredField; - fieldsCache.put(fieldName, field); - break; - } - } - klassRef = klassRef.getSuperKlass(); - } - if (field == null) { - throw new NoSuchFieldException(); - } - } - return InternalRedefinitionPlugin.createUncached(field.get(theInstance)); - } -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java deleted file mode 100644 index b85a6de7d2bb..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineObjectImpl.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; - -import com.oracle.truffle.espresso.descriptors.Symbol; -import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.jdwp.api.KlassRef; -import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; -import com.oracle.truffle.espresso.runtime.EspressoContext; -import com.oracle.truffle.espresso.runtime.StaticObject; - -import java.lang.ref.WeakReference; - -public abstract class RedefineObjectImpl implements RedefineObject { - - protected final WeakReference instance; - protected final Klass klass; - - protected RedefineObjectImpl(StaticObject object) { - this.instance = new WeakReference<>(object); - this.klass = object.getKlass(); - } - - public RedefineObjectImpl(KlassRef klass) { - this.instance = new WeakReference<>(StaticObject.NULL); - this.klass = (Klass) klass; - } - - @Override - public Klass getKlass() { - return klass; - } - - @Override - public Object getRawValue() { - return instance.get(); - } - - @Override - public abstract Object invoke(String name, RedefineObject... args) throws NoSuchMethodException; - - @SuppressWarnings("unused") - public RedefineObject invokePrecise(String className, String methodName, RedefineObject... args) throws NoSuchMethodException { - throw new NoSuchMethodException(className + "." + methodName); - } - - @Override - public abstract RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException; - - protected Method lookupMethod(String name, RedefineObject[] args) { - StaticObject theInstance = instance.get(); - if (theInstance == null) { - throw new IllegalStateException("cannot invoke method on garbage collected instance"); - } - - Klass currentKlass = klass; - - while (currentKlass != null) { - Method method = lookupMethod(currentKlass, name, args); - if (method != null) { - return method; - } - Klass[] interfaces = currentKlass.getTransitiveInterfacesList(); - for (Klass itf : interfaces) { - method = lookupMethod(itf, name, args); - if (method != null && !method.isAbstract()) { - return method; - } - } - currentKlass = currentKlass.getSuperKlass(); - } - return null; - } - - protected Method lookupMethod(Klass klassRef, String name, RedefineObject[] args) { - for (Method declaredMethod : klassRef.getDeclaredMethods()) { - if (declaredMethod.getNameAsString().equals(name)) { - // match arguments - boolean match = true; - Klass[] parameters = declaredMethod.resolveParameterKlasses(); - if (declaredMethod.getParameterCount() == args.length) { - for (int i = 0; i < args.length; i++) { - if (args[i] == null) { - if (parameters[i].isPrimitive()) { - match = false; - break; - } - continue; - } - if (!parameters[i].isAssignableFrom((Klass) args[i].getKlass())) { - match = false; - break; - } - } - } else if (declaredMethod.isVarargs()) { - int parameterCount = declaredMethod.getParameterCount(); - int argsCount = args.length; - - if (parameterCount == 1) { - // pure varargs - // TODO - implement this case - match = false; - } else { - // match before varargs param - for (int i = 0; i < parameterCount - 1; i++) { - if (args[i] == null) { - if (parameters[i].isPrimitive()) { - match = false; - break; - } - continue; - } - if (!parameters[i].isAssignableFrom((Klass) args[i].getKlass())) { - match = false; - break; - } - } - // match varargs - Klass varargsParam = parameters[parameterCount - 1]; - for (int i = parameterCount - 1; i < argsCount; i++) { - if (args[i] == null) { - if (varargsParam.isPrimitive()) { - match = false; - break; - } - continue; - } - if (varargsParam.isAssignableFrom((Klass) args[i].getKlass())) { - match = false; - break; - } - } - } - } else { - // arguments didn't match - match = false; - } - if (match) { - return declaredMethod; - } - } - } - return null; - } - - protected Object[] rawObjects(RedefineObjectImpl[] args) { - Object[] result = new Object[args.length]; - for (int i = 0; i < args.length; i++) { - result[i] = args[i].instance.get(); - } - return result; - } - - public RedefineObject fromType(String className) { - StaticObject theInstance = (StaticObject) getRawValue(); - if (theInstance == null || theInstance.getKlass() == null) { - return null; - } - EspressoContext context = theInstance.getKlass().getContext(); - Symbol type = context.getTypes().fromClassGetName(className); - Klass loadedClass = context.getRegistries().findLoadedClass(type, theInstance.getKlass().getDefiningClassLoader()); - if (loadedClass != null) { - return new CachedRedefineObject(loadedClass.mirror()); - } - return null; - } -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java index 0c538b0bd97a..348b6984c219 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -39,7 +39,6 @@ import com.oracle.truffle.espresso.redefinition.ClassLoadListener; import com.oracle.truffle.espresso.redefinition.plugins.api.ClassLoadAction; import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; -import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; @@ -47,16 +46,14 @@ public final class RedefinitionPluginHandler implements RedefineListener, ClassL private final EspressoContext context; private final Set internalPlugins = Collections.synchronizedSet(new HashSet<>(1)); - private final Map, Set> internalTriggers; private final Map, List> classLoadActions = Collections.synchronizedMap(new HashMap<>()); // The guest language HotSwap plugin handler passed // onto us if guest plugins are present at runtime. private ExternalPluginHandler externalPluginHandler; - private RedefinitionPluginHandler(EspressoContext espressoContext, Map, Set> triggers) { + private RedefinitionPluginHandler(EspressoContext espressoContext) { this.context = espressoContext; - this.internalTriggers = triggers; } @TruffleBoundary @@ -78,42 +75,29 @@ public void registerExternalHotSwapHandler(StaticObject handler) { public static RedefinitionPluginHandler create(EspressoContext espressoContext) { // we use ServiceLoader to load all Espresso internal Plugins + RedefinitionPluginHandler handler = new RedefinitionPluginHandler(espressoContext); ServiceLoader serviceLoader = ServiceLoader.load(InternalRedefinitionPlugin.class); Iterator pluginIterator = serviceLoader.iterator(); - Map, Set> triggers = new HashMap<>(); while (pluginIterator.hasNext()) { InternalRedefinitionPlugin plugin = pluginIterator.next(); - for (TriggerClass triggerClass : plugin.getTriggerClasses()) { - Symbol triggerType = triggerClass.getTriggerType(); - Set triggerClasses = triggers.get(triggerType); - if (triggerClasses == null) { - triggerClasses = new HashSet<>(1); - } - triggerClasses.add(triggerClass); - triggers.put(triggerType, triggerClasses); - } + handler.activatePlugin(plugin); + espressoContext.registerRedefinitionPlugin(plugin); } - RedefinitionPluginHandler handler = new RedefinitionPluginHandler(espressoContext, triggers); espressoContext.getRegistries().registerListener(handler); return handler; } + private void activatePlugin(InternalRedefinitionPlugin plugin) { + internalPlugins.add(plugin); + plugin.activate(context, this); + } + @TruffleBoundary @Override public void onClassLoad(ObjectKlass klass) { // internal plugins Symbol type = klass.getType(); - if (internalTriggers.containsKey(type)) { - Set triggerClasses = internalTriggers.get(type); - for (TriggerClass triggerClass : triggerClasses) { - if (!internalPlugins.contains(triggerClass.getPlugin())) { - triggerClass.getPlugin().activate(klass.getContext(), this); - internalPlugins.add(triggerClass.getPlugin()); - } - triggerClass.fire(klass); - } - } // fire registered load actions List loadActions = classLoadActions.getOrDefault(type, Collections.emptyList()); Iterator it = loadActions.iterator(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java deleted file mode 100644 index 4b5729388ddc..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/UncachedRedefineObject.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition.plugins.impl; - -import com.oracle.truffle.espresso.impl.Field; -import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; -import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; -import com.oracle.truffle.espresso.runtime.StaticObject; - -public final class UncachedRedefineObject extends RedefineObjectImpl { - public UncachedRedefineObject(StaticObject object) { - super(object); - } - - @Override - public Object invoke(String name, RedefineObject... args) throws NoSuchMethodException { - Method method = lookupMethod(name, args); - if (method != null) { - RedefineObjectImpl[] internalArgs = new RedefineObjectImpl[args.length]; - for (int i = 0; i < args.length; i++) { - internalArgs[i] = (RedefineObjectImpl) args[i]; - } - return method.invokeDirect(instance.get(), rawObjects(internalArgs)); - } - throw new NoSuchMethodException(); - } - - @Override - public RedefineObject getInstanceField(String fieldName) throws NoSuchFieldException { - StaticObject theInstance = instance.get(); - if (theInstance == null) { - throw new IllegalStateException("cannot get field on garbage collection instance"); - } - Klass klassRef = klass; - while (klassRef != null) { - for (Field declaredField : klassRef.getDeclaredFields()) { - if (declaredField.getNameAsString().equals(fieldName)) { - return InternalRedefinitionPlugin.createUncached(declaredField.get(theInstance)); - } - } - klassRef = klassRef.getSuperKlass(); - } - throw new NoSuchFieldException(); - } -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java index 575ce7af0d20..cb058372d254 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java @@ -23,63 +23,44 @@ package com.oracle.truffle.espresso.redefinition.plugins.jdkcaches; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.oracle.truffle.espresso.descriptors.Symbol; -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.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; -import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; -import com.oracle.truffle.espresso.redefinition.plugins.api.RedefineObject; -import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; +import com.oracle.truffle.espresso.redefinition.plugins.impl.RedefinitionPluginHandler; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.StaticObject; public final class JDKCacheRedefinitionPlugin extends InternalRedefinitionPlugin { - public static final Symbol INTROSPECTOR_CLASS = Symbol.Type.java_beans_Introspector; - public static final Symbol FLUSH_CACHES_METHOD = Symbol.Name.flushFromCaches; - private MethodRef flushFromCachesMethod; - - public static final Symbol THREAD_GROUP_CONTEXT = Symbol.Type.java_beans_ThreadGroupContext; - public static final String REMOVE_BEAN_INFO = "removeBeanInfo"; - private List threadGroupContext = Collections.synchronizedList(new ArrayList<>(1)); - - @Override - public String getName() { - return "JDK Cache Flushing Plugin"; - } + private List> threadGroupContexts = Collections.synchronizedList(new ArrayList<>(4)); + private Method flushFromCachesMethod; + private Method removeBeanInfoMethod; @Override - public TriggerClass[] getTriggerClasses() { - TriggerClass[] triggerClasses = new TriggerClass[2]; - triggerClasses[0] = new TriggerClass(INTROSPECTOR_CLASS, this, klass -> { - hookMethodEntry(klass, new MethodLocator(FLUSH_CACHES_METHOD, Symbol.Signature._void_Class), MethodHook.Kind.ONE_TIME, - ((method, variables) -> flushFromCachesMethod = method)); - }); - triggerClasses[1] = new TriggerClass(THREAD_GROUP_CONTEXT, this, klass -> { - hookConstructor(klass, MethodHook.Kind.INDEFINITE, ((method, variables) -> { - threadGroupContext.add(InternalRedefinitionPlugin.createCached(variables[0].getValue())); - })); - }); - return triggerClasses; + public void activate(EspressoContext espressoContext, RedefinitionPluginHandler handler) { + super.activate(espressoContext, handler); + flushFromCachesMethod = espressoContext.getMeta().java_beans_Introspector_flushFromCaches; + removeBeanInfoMethod = espressoContext.getMeta().java_beans_ThreadGroupContext_removeBeanInfo; } @Override - public void postClassRedefinition(KlassRef[] changedKlasses) { - for (KlassRef changedKlass : changedKlasses) { - Object guestKlass = getGuestClassInstance(changedKlass); + public void postClassRedefinition(ObjectKlass[] changedKlasses) { + for (ObjectKlass changedKlass : changedKlasses) { if (flushFromCachesMethod != null) { - flushFromCachesMethod.invokeMethod(null, new Object[]{guestKlass}); + flushFromCachesMethod.invokeDirect(null, changedKlass.mirror()); } - for (RedefineObject context : threadGroupContext) { - try { - context.invoke(REMOVE_BEAN_INFO, InternalRedefinitionPlugin.createUncached(guestKlass)); - } catch (NoSuchMethodException e) { - // TODO - add logging - } + for (WeakReference context : threadGroupContexts) { + removeBeanInfoMethod.invokeDirect(context, changedKlass.mirror()); } } } + + public synchronized void registerThreadGroupContext(StaticObject context) { + threadGroupContexts.add(new WeakReference<>(context)); + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index 19ebb73d4933..24562329bff0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -29,94 +29,47 @@ import java.util.Map; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.interop.InvalidArrayIndexException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.ObjectKlass; 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.MethodVariable; import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; -import com.oracle.truffle.espresso.redefinition.plugins.api.MethodLocator; -import com.oracle.truffle.espresso.redefinition.plugins.api.TriggerClass; +import com.oracle.truffle.espresso.runtime.StaticObject; +import com.oracle.truffle.espresso.substitutions.Host; public final class JDKProxyRedefinitionPlugin extends InternalRedefinitionPlugin { - private static final Symbol PROXY_GENERATOR_CLASS = Symbol.Type.sun_misc_ProxyGenerator; - private static final Symbol GENERATOR_METHOD = Symbol.Name.generateProxyClass; - - private static final Symbol PROXY_SUPER_CLASS = Symbol.Type.java_lang_reflect_Proxy; private final Map> cache = Collections.synchronizedMap(new HashMap<>()); - - private MethodRef proxyGeneratorMethod; - private KlassRef proxySuperKlass; - - private ThreadLocal generationInProgress = ThreadLocal.withInitial(() -> false); - - @Override - public String getName() { - return "JDK Dynamic Proxy Reloading Plugin"; - } - - @Override - public TriggerClass[] getTriggerClasses() { - TriggerClass[] triggerClasses = new TriggerClass[2]; - // trigger on proxy generator class and add generator method hooks - triggerClasses[0] = new TriggerClass(PROXY_GENERATOR_CLASS, this, klass -> { - // hook into the proxy generator method to obtain proxy generation arguments - hookMethodEntry(klass, new MethodLocator(GENERATOR_METHOD, Symbol.Signature._byte_array_String_Class_array_int), MethodHook.Kind.INDEFINITE, (method, variables) -> { - if (generationInProgress.get()) { - // don't hook when we're re-generating proxy bytes - return; - } - if (proxyGeneratorMethod == null) { - proxyGeneratorMethod = method; - } - collectProxyArguments(variables); - }); - }); - // trigger on Proxy super class to obtain the type - triggerClasses[1] = new TriggerClass(PROXY_SUPER_CLASS, this, klass -> proxySuperKlass = klass); - return triggerClasses; - } - - private synchronized void collectProxyArguments(MethodVariable[] variables) { - Object[] proxyArgs = new Object[3]; - // proxy name - proxyArgs[0] = variables[0].getValue(); - // proxy interfaces - proxyArgs[1] = variables[1].getValue(); - // proxy access modifiers - proxyArgs[2] = variables[2].getValue(); - - try { - // fetch klass instances for the declared proxy interfaces - Object interfaces = proxyArgs[1]; - long arraySize = INTEROP.getArraySize(interfaces); - KlassRef[] proxyInterfaces = new KlassRef[(int) arraySize]; - for (int i = 0; i < arraySize; i++) { - // get the klass type of the interface - proxyInterfaces[i] = getreflectedKlassType(INTEROP.readArrayElement(interfaces, i)); - } - - // register onLoad action that will give us - // the klass object for the generated proxy - String proxyName = proxyArgs[0].toString(); - registerClassLoadAction(proxyName, klass -> { - ProxyCache proxyCache = new ProxyCache(klass, proxyArgs); - - // cache proxy arguments under each interface, so that - // when they change we can re-generate the proxy bytes - for (KlassRef proxyInterface : proxyInterfaces) { - addCacheEntry(proxyCache, proxyInterface); - } - }); - } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - // TODO - log here. Should we have a dedicated HotSwap logger that logs to file? + private DirectCallNode proxyGeneratorMethodCallNode; + + public synchronized void collectProxyArguments(@Host(String.class) StaticObject proxyName, + @Host(Class[].class) StaticObject interfaces, + int classModifier, + DirectCallNode generatorMethodCallNode) { + if (proxyGeneratorMethodCallNode == null) { + proxyGeneratorMethodCallNode = generatorMethodCallNode; } + // register onLoad action that will give us + // the klass object for the generated proxy + registerClassLoadAction(getContext().getMeta().toHostString(proxyName), klass -> { + // store guest-world arguments that we can use when + // invoking the call node later on re-generation + ProxyCache proxyCache = new ProxyCache(klass, proxyName, interfaces, classModifier); + + Klass[] proxyInterfaces = new Klass[interfaces.length()]; + for (int i = 0; i < proxyInterfaces.length; i++) { + proxyInterfaces[i] = (Klass) getContext().getMeta().HIDDEN_MIRROR_KLASS.getHiddenObject(interfaces.get(i)); + } + // cache proxy arguments under each interface, so that + // when they change we can re-generate the proxy bytes + for (KlassRef proxyInterface : proxyInterfaces) { + addCacheEntry(proxyCache, proxyInterface); + } + }); } + @TruffleBoundary private void addCacheEntry(ProxyCache proxyCache, KlassRef proxyInterface) { List list = cache.get(proxyInterface); if (list == null) { @@ -134,9 +87,8 @@ public synchronized void fillExtraReloadClasses(List redefineInfos if (klass != null) { List list = cache.getOrDefault(klass, Collections.emptyList()); for (ProxyCache proxyCache : list) { - generationInProgress.set(true); - byte[] proxyBytes = (byte[]) proxyGeneratorMethod.invokeMethod(null, proxyCache.proxyArgs); - generationInProgress.set(false); + StaticObject result = (StaticObject) proxyGeneratorMethodCallNode.call(proxyCache.proxyName, proxyCache.interfaces, proxyCache.classModifier); + byte[] proxyBytes = (byte[]) getContext().getMeta().toHostBoxed(result); additional.add(new RedefineInfo(proxyCache.klass, proxyBytes)); } } @@ -144,20 +96,23 @@ public synchronized void fillExtraReloadClasses(List redefineInfos } @Override - public boolean reRunClinit(KlassRef klass, boolean changed) { + public boolean reRunClinit(ObjectKlass klass, boolean changed) { // changed Dynamic Proxy classes have cached Method references // in static fields, so always re-run the static initializer - return proxySuperKlass.isAssignable(klass); + return changed && getContext().getMeta().java_lang_reflect_Proxy.isAssignable(klass); } private final class ProxyCache { private final KlassRef klass; - private final Object[] proxyArgs; + private final StaticObject proxyName; + private final StaticObject interfaces; + private final int classModifier; - ProxyCache(KlassRef klass, Object[] proxyArgs) { - assert proxyArgs.length == 3; + ProxyCache(KlassRef klass, StaticObject proxyName, StaticObject interfaces, int classModifier) { this.klass = klass; - this.proxyArgs = proxyArgs; + this.proxyName = proxyName; + this.interfaces = interfaces; + this.classModifier = classModifier; } } } 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 5dfa0c9eebf9..cca92a6a0b4f 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 @@ -31,6 +31,8 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -44,6 +46,7 @@ import java.util.stream.Collectors; import com.oracle.truffle.espresso.FinalizationFeature; +import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; import org.graalvm.options.OptionMap; import org.graalvm.polyglot.Engine; @@ -51,6 +54,7 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.TruffleLanguage; @@ -149,6 +153,8 @@ public final class EspressoContext { private VMListener eventListener; // endregion JDWP + private Map, InternalRedefinitionPlugin> redefinitionPlugins; + // region Options // Checkstyle: stop field name check @@ -720,6 +726,20 @@ public void prepareDispose() { } } + public void registerRedefinitionPlugin(InternalRedefinitionPlugin plugin) { + // lazy initialization + if (redefinitionPlugins == null) { + redefinitionPlugins = Collections.synchronizedMap(new HashMap<>(2)); + } + redefinitionPlugins.put(plugin.getClass(), plugin); + } + + @SuppressWarnings("unchecked") + @TruffleBoundary + public T lookup(Class pluginType) { + return (T) redefinitionPlugins.get(pluginType); + } + // region Agents public TruffleObject bindToAgent(Method method, String mangledName) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_beans_ThreadGroupContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_beans_ThreadGroupContext.java new file mode 100644 index 000000000000..395ce44de46f --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_beans_ThreadGroupContext.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021, 2021, 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.substitutions; + +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.plugins.jdkcaches.JDKCacheRedefinitionPlugin; +import com.oracle.truffle.espresso.runtime.StaticObject; + +@EspressoSubstitutions +public class Target_java_beans_ThreadGroupContext { + + @Substitution(hasReceiver = true, methodName = "") + public static void init( + @Host(Class.class) StaticObject context, + // Checkstyle: stop + @GuestCall(target = "java_beans_ThreadGroupContext_init", original = true) DirectCallNode original, + // Checkstyle: resume + @InjectMeta Meta meta) { + + // for class redefinition we need to collect details about beans + JDKCacheRedefinitionPlugin plugin = meta.getContext().lookup(JDKCacheRedefinitionPlugin.class); + if (plugin != null) { + plugin.registerThreadGroupContext(context); + } + // call original method + original.call(context); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_ProxyGenerator.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_ProxyGenerator.java new file mode 100644 index 000000000000..eb0f20321d34 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_ProxyGenerator.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021, 2021, 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.substitutions; + +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.plugins.jdkproxy.JDKProxyRedefinitionPlugin; +import com.oracle.truffle.espresso.runtime.StaticObject; + +@EspressoSubstitutions +public class Target_java_lang_reflect_ProxyGenerator { + + @Substitution(versionFilter = VersionFilter.Java8OrEarlier.class) + public static @Host(byte[].class) StaticObject generateProxyClass( + @Host(String.class) StaticObject proxyName, + @Host(Class[].class) StaticObject interfaces, + int classModifier, + // Checkstyle: stop + @GuestCall(target = "java_lang_reflect_ProxyGenerator_generateProxyClass", original = true) DirectCallNode original, + // Checkstyle: resume + @InjectMeta Meta meta) { + + // for class redefinition we need to collect details about generated JDK Dynamic proxies + JDKProxyRedefinitionPlugin plugin = meta.getContext().lookup(JDKProxyRedefinitionPlugin.class); + if (plugin != null) { + plugin.collectProxyArguments(proxyName, interfaces, classModifier, original); + } + // call original method + return (StaticObject) original.call(proxyName, interfaces, classModifier); + } +} + diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_misc_ProxyGenerator.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_misc_ProxyGenerator.java new file mode 100644 index 000000000000..1a25cbab1577 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_misc_ProxyGenerator.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, 2021, 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.substitutions; + +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.redefinition.plugins.jdkproxy.JDKProxyRedefinitionPlugin; +import com.oracle.truffle.espresso.runtime.StaticObject; + +@EspressoSubstitutions +public final class Target_sun_misc_ProxyGenerator { + + @Substitution(versionFilter = VersionFilter.Java8OrEarlier.class) + public static @Host(byte[].class) StaticObject generateProxyClass( + @Host(String.class) StaticObject proxyName, + @Host(Class[].class) StaticObject interfaces, + int classModifier, + // Checkstyle: stop + @GuestCall(target = "sun_misc_ProxyGenerator_generateProxyClass", original = true) DirectCallNode original, + // Checkstyle: resume + @InjectMeta Meta meta) { + + // for class redefinition we need to collect details about generated JDK Dynamic proxies + JDKProxyRedefinitionPlugin plugin = meta.getContext().lookup(JDKProxyRedefinitionPlugin.class); + if (plugin != null) { + plugin.collectProxyArguments(proxyName, interfaces, classModifier, original); + } + // call original method + return (StaticObject) original.call(proxyName, interfaces, classModifier); + } +} From 4a2dca18c36fa1c6d2fc3c961e0e9d65077c4755 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 22 Apr 2021 18:13:37 +0200 Subject: [PATCH 034/290] Add truffle boundary. Fix a post hotswap plugin error. Make sure all plugins run their post HotSwap actions regardless of other plugin errors. --- .../plugins/impl/RedefinitionPluginHandler.java | 7 ++++++- .../plugins/jdkcaches/JDKCacheRedefinitionPlugin.java | 5 ++++- ...com_oracle_truffle_espresso_hotswap_HotSwapHandler.java | 5 ++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java index 348b6984c219..33d6dbb14e54 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -133,7 +133,12 @@ public boolean rerunClinit(ObjectKlass klass, boolean changed) { public void postRedefition(ObjectKlass[] changedKlasses) { // internal plugins for (InternalRedefinitionPlugin plugin : internalPlugins) { - plugin.postClassRedefinition(changedKlasses); + try { + plugin.postClassRedefinition(changedKlasses); + } catch (Throwable t) { + // don't let individual plugin errors cause failure + // to run other post redefinition plugins + } } // external plugins if (externalPluginHandler != null) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java index cb058372d254..96d0c9b70015 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.List; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; @@ -54,12 +55,14 @@ public void postClassRedefinition(ObjectKlass[] changedKlasses) { if (flushFromCachesMethod != null) { flushFromCachesMethod.invokeDirect(null, changedKlass.mirror()); } - for (WeakReference context : threadGroupContexts) { + for (WeakReference ref : threadGroupContexts) { + StaticObject context = ref.get(); removeBeanInfoMethod.invokeDirect(context, changedKlass.mirror()); } } } + @TruffleBoundary public synchronized void registerThreadGroupContext(StaticObject context) { threadGroupContexts.add(new WeakReference<>(context)); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java index b0a8faba2141..dba71ae553c7 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler.java @@ -26,12 +26,11 @@ import com.oracle.truffle.espresso.runtime.StaticObject; @EspressoSubstitutions -public final class Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler { +final class Target_com_oracle_truffle_espresso_hotswap_HotSwapHandler { @Substitution - public static boolean registerHandler(@Host(Object.class) StaticObject handler, @InjectMeta Meta meta) { + static boolean registerHandler(@Host(Object.class) StaticObject handler, @InjectMeta Meta meta) { assert handler != null; - if (meta.getContext().JDWPOptions == null) { // only allow HotSwap handler registration when running in debug mode return false; From 795fe3bda74944e9e5cdaeb322ebc8d5b4f842f1 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 22 Apr 2021 18:18:15 +0200 Subject: [PATCH 035/290] add null check --- .../plugins/jdkcaches/JDKCacheRedefinitionPlugin.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java index 96d0c9b70015..66c8cbafb595 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java @@ -57,7 +57,9 @@ public void postClassRedefinition(ObjectKlass[] changedKlasses) { } for (WeakReference ref : threadGroupContexts) { StaticObject context = ref.get(); - removeBeanInfoMethod.invokeDirect(context, changedKlass.mirror()); + if (context != null) { + removeBeanInfoMethod.invokeDirect(context, changedKlass.mirror()); + } } } } From 6ee601dd01b496a43faf1189cce52af91c2305aa Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Fri, 23 Apr 2021 08:43:42 +0200 Subject: [PATCH 036/290] formatting pass --- .../Target_java_beans_ThreadGroupContext.java | 10 +++++----- .../Target_java_lang_reflect_ProxyGenerator.java | 15 +++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_beans_ThreadGroupContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_beans_ThreadGroupContext.java index 395ce44de46f..7320605c7032 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_beans_ThreadGroupContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_beans_ThreadGroupContext.java @@ -32,11 +32,11 @@ public class Target_java_beans_ThreadGroupContext { @Substitution(hasReceiver = true, methodName = "") public static void init( - @Host(Class.class) StaticObject context, - // Checkstyle: stop - @GuestCall(target = "java_beans_ThreadGroupContext_init", original = true) DirectCallNode original, - // Checkstyle: resume - @InjectMeta Meta meta) { + @Host(Class.class) StaticObject context, + // Checkstyle: stop + @GuestCall(target = "java_beans_ThreadGroupContext_init", original = true) DirectCallNode original, + // Checkstyle: resume + @InjectMeta Meta meta) { // for class redefinition we need to collect details about beans JDKCacheRedefinitionPlugin plugin = meta.getContext().lookup(JDKCacheRedefinitionPlugin.class); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_ProxyGenerator.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_ProxyGenerator.java index eb0f20321d34..4012b9e8cdc8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_ProxyGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_ProxyGenerator.java @@ -32,13 +32,13 @@ public class Target_java_lang_reflect_ProxyGenerator { @Substitution(versionFilter = VersionFilter.Java8OrEarlier.class) public static @Host(byte[].class) StaticObject generateProxyClass( - @Host(String.class) StaticObject proxyName, - @Host(Class[].class) StaticObject interfaces, - int classModifier, - // Checkstyle: stop - @GuestCall(target = "java_lang_reflect_ProxyGenerator_generateProxyClass", original = true) DirectCallNode original, - // Checkstyle: resume - @InjectMeta Meta meta) { + @Host(String.class) StaticObject proxyName, + @Host(Class[].class) StaticObject interfaces, + int classModifier, + // Checkstyle: stop + @GuestCall(target = "java_lang_reflect_ProxyGenerator_generateProxyClass", original = true) DirectCallNode original, + // Checkstyle: resume + @InjectMeta Meta meta) { // for class redefinition we need to collect details about generated JDK Dynamic proxies JDKProxyRedefinitionPlugin plugin = meta.getContext().lookup(JDKProxyRedefinitionPlugin.class); @@ -49,4 +49,3 @@ public class Target_java_lang_reflect_ProxyGenerator { return (StaticObject) original.call(proxyName, interfaces, classModifier); } } - From 6b5b23a88c96d76db6bb8303bd1a197f2e257914 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Fri, 23 Apr 2021 09:24:40 +0200 Subject: [PATCH 037/290] don't assume java.beans classes are available at runtime --- .../src/com/oracle/truffle/espresso/meta/Meta.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java index 9eb3a14bb885..9e82206d2849 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java @@ -649,11 +649,14 @@ public Meta(EspressoContext context) { // used for class redefinition java_lang_reflect_Proxy = knownKlass(Type.java_lang_reflect_Proxy); - java_beans_ThreadGroupContext = knownKlass(Type.java_beans_ThreadGroupContext); - java_beans_ThreadGroupContext_init = java_beans_ThreadGroupContext.requireDeclaredMethod(Name._init_, Signature._void); - java_beans_ThreadGroupContext_removeBeanInfo = java_beans_ThreadGroupContext.requireDeclaredMethod(Name.removeBeanInfo, Signature._void_Class); - java_beans_Introspector = knownKlass(Type.java_beans_Introspector); - java_beans_Introspector_flushFromCaches = java_beans_Introspector.requireDeclaredMethod(Name.flushFromCaches, Signature._void_Class); + + // java.beans package only available if java.desktop module is present on JDK9+ + java_beans_ThreadGroupContext = loadKlassWithBootClassLoader(Type.java_beans_ThreadGroupContext); + java_beans_Introspector = loadKlassWithBootClassLoader(Type.java_beans_Introspector); + + java_beans_ThreadGroupContext_init = java_beans_ThreadGroupContext != null ? java_beans_ThreadGroupContext.requireDeclaredMethod(Name._init_, Signature._void) : null; + java_beans_ThreadGroupContext_removeBeanInfo = java_beans_ThreadGroupContext != null ? java_beans_ThreadGroupContext.requireDeclaredMethod(Name.removeBeanInfo, Signature._void_Class) : null; + java_beans_Introspector_flushFromCaches = java_beans_Introspector != null ? java_beans_Introspector.requireDeclaredMethod(Name.flushFromCaches, Signature._void_Class) : null; // sun.misc.Proxygenerator -> java.lang.reflect.Proxygenerator in JDK 9 if (getJavaVersion().java8OrEarlier()) { From bd51baededd1cb0ebe24150a3718ea90e53bf287 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 26 Apr 2021 08:06:35 +0200 Subject: [PATCH 038/290] make sure child nodes arrays in BytecodeNode have instrumentable types --- .../truffle/espresso/nodes/BytecodeNode.java | 41 ++++++++++--------- .../EspressoInstrumentableQuickNode.java | 6 +++ .../espresso/nodes/EspressoRootNode.java | 8 ---- .../espresso/nodes/quick/QuickNode.java | 6 --- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index cf981d495ccf..ea64dc164822 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -369,8 +369,9 @@ public final class BytecodeNode extends EspressoMethodNode { assert Integer.bitCount(REPORT_LOOP_STRIDE) == 1 : "must be a power of 2"; } - @Children private QuickNode[] nodes = QuickNode.EMPTY_ARRAY; - @Children private QuickNode[] sparseNodes = QuickNode.EMPTY_ARRAY; + // must not be of type QuickNode as it might be wrapped by instrumentation + @Children private EspressoInstrumentableQuickNode[] nodes = QuickNode.EMPTY_ARRAY; + @Children private EspressoInstrumentableQuickNode[] sparseNodes = QuickNode.EMPTY_ARRAY; /** * Ideally, we would want one such node per AASTORE bytecode. Unfortunately, the AASTORE * bytecode is a single byte long, so we cannot quicken it, and it is far too common to pay for @@ -1079,7 +1080,7 @@ Object executeBody(VirtualFrame frame) { CompilerDirectives.transferToInterpreterAndInvalidate(); continue loop; } - QuickNode quickNode = nodes[readCPI(curBCI)]; + EspressoInstrumentableQuickNode quickNode = nodes[readCPI(curBCI)]; if (quickNode.removedByRedefintion()) { CompilerDirectives.transferToInterpreterAndInvalidate(); synchronized (this) { @@ -1212,7 +1213,7 @@ Object executeBody(VirtualFrame frame) { } // This check includes newly rewritten QUICK nodes, not just curOpcode == quick if (noForeignObjects.isValid() && (bs.currentBC(curBCI) == QUICK || bs.currentBC(curBCI) == SLIM_QUICK)) { - QuickNode quickNode; + EspressoInstrumentableQuickNode quickNode; if (bs.currentBC(curBCI) == QUICK) { quickNode = nodes[readCPI(curBCI)]; } else { @@ -1568,7 +1569,7 @@ private char readOriginalCPI(int curBCI) { return BytecodeStream.readCPI(getMethodVersion().getOriginalCode(), curBCI); } - private char addQuickNode(QuickNode node) { + private char addQuickNode(EspressoInstrumentableQuickNode node) { CompilerAsserts.neverPartOfCompilation(); Objects.requireNonNull(node); nodes = Arrays.copyOf(nodes, nodes.length + 1); @@ -1577,7 +1578,7 @@ private char addQuickNode(QuickNode node) { return (char) nodeIndex; } - private void addSlimQuickNode(QuickNode node, int curBCI) { + private void addSlimQuickNode(EspressoInstrumentableQuickNode node, int curBCI) { CompilerAsserts.neverPartOfCompilation(); Objects.requireNonNull(node); if (sparseNodes == QuickNode.EMPTY_ARRAY) { @@ -1605,7 +1606,7 @@ private void patchBci(int bci, byte opcode, char nodeIndex) { VolatileArrayAccess.volatileWrite(code, bci, opcode); } - private QuickNode injectQuick(int curBCI, QuickNode quick, int opcode) { + private EspressoInstrumentableQuickNode injectQuick(int curBCI, EspressoInstrumentableQuickNode quick, int opcode) { QUICKENED_BYTECODES.inc(); CompilerAsserts.neverPartOfCompilation(); if (opcode == SLIM_QUICK) { @@ -1618,7 +1619,7 @@ private QuickNode injectQuick(int curBCI, QuickNode quick, int opcode) { return quick; } - private QuickNode tryPatchQuick(int curBCI, Supplier newQuickNode) { + private EspressoInstrumentableQuickNode tryPatchQuick(int curBCI, Supplier newQuickNode) { synchronized (this) { if (bs.currentVolatileBC(curBCI) == QUICK) { return nodes[readCPI(curBCI)]; @@ -1635,7 +1636,7 @@ private int quickenCheckCast(VirtualFrame frame, long[] primitives, Object[] ref } CompilerDirectives.transferToInterpreterAndInvalidate(); assert opcode == CHECKCAST; - QuickNode quick = tryPatchQuick(curBCI, () -> new CheckCastNode(resolveType(CHECKCAST, readCPI(curBCI)), top, curBCI)); + EspressoInstrumentableQuickNode quick = tryPatchQuick(curBCI, () -> new CheckCastNode(resolveType(CHECKCAST, readCPI(curBCI)), top, curBCI)); return quick.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(opcode); } @@ -1647,7 +1648,7 @@ private int quickenInstanceOf(VirtualFrame frame, long[] primitives, Object[] re } CompilerDirectives.transferToInterpreterAndInvalidate(); assert opcode == INSTANCEOF; - QuickNode quick = tryPatchQuick(curBCI, () -> new InstanceOfNode(resolveType(CHECKCAST, readCPI(curBCI)), top, curBCI)); + EspressoInstrumentableQuickNode quick = tryPatchQuick(curBCI, () -> new InstanceOfNode(resolveType(CHECKCAST, readCPI(curBCI)), top, curBCI)); return quick.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(opcode); } @@ -1655,7 +1656,7 @@ private int quickenInvoke(VirtualFrame frame, long[] primitives, Object[] refs, QUICKENED_INVOKES.inc(); CompilerDirectives.transferToInterpreterAndInvalidate(); assert Bytecodes.isInvoke(opcode); - QuickNode quick = tryPatchQuick(curBCI, () -> { + EspressoInstrumentableQuickNode quick = tryPatchQuick(curBCI, () -> { // During resolution of the symbolic reference to the method, any of the exceptions // pertaining to method resolution (§5.4.3.3) can be thrown. char cpi = readCPI(curBCI); @@ -1673,7 +1674,7 @@ private int quickenInvoke(VirtualFrame frame, long[] primitives, Object[] refs, public int reQuickenInvoke(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int opcode, int statementIndex, Method resolutionSeed) { CompilerDirectives.transferToInterpreterAndInvalidate(); assert Bytecodes.isInvoke(opcode); - QuickNode invoke = null; + EspressoInstrumentableQuickNode invoke = null; synchronized (this) { assert bs.currentBC(curBCI) == QUICK; char cpi = readCPI(curBCI); @@ -1689,20 +1690,20 @@ public int reQuickenInvoke(VirtualFrame frame, long[] primitives, Object[] refs, public int quickenGetField(final VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int opcode, int statementIndex, Field field) { CompilerDirectives.transferToInterpreterAndInvalidate(); assert opcode == GETFIELD; - QuickNode getField = tryPatchQuick(curBCI, () -> new QuickenedGetFieldNode(top, curBCI, statementIndex, field)); + EspressoInstrumentableQuickNode getField = tryPatchQuick(curBCI, () -> new QuickenedGetFieldNode(top, curBCI, statementIndex, field)); return getField.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(opcode); } public int quickenPutField(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int opcode, int statementIndex, Field field) { CompilerDirectives.transferToInterpreterAndInvalidate(); assert opcode == PUTFIELD; - QuickNode putField = tryPatchQuick(curBCI, () -> new QuickenedPutFieldNode(top, curBCI, field, statementIndex)); + EspressoInstrumentableQuickNode putField = tryPatchQuick(curBCI, () -> new QuickenedPutFieldNode(top, curBCI, field, statementIndex)); return putField.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(opcode); } private int quickenArrayLength(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI) { CompilerDirectives.transferToInterpreterAndInvalidate(); - QuickNode arrayLengthNode; + EspressoInstrumentableQuickNode arrayLengthNode; synchronized (this) { if (bs.currentVolatileBC(curBCI) == SLIM_QUICK) { arrayLengthNode = sparseNodes[curBCI]; @@ -1715,7 +1716,7 @@ private int quickenArrayLength(VirtualFrame frame, long[] primitives, Object[] r private int quickenArrayLoad(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int loadOpcode, JavaKind componentKind) { CompilerDirectives.transferToInterpreterAndInvalidate(); - QuickNode arrayLoadNode; + EspressoInstrumentableQuickNode arrayLoadNode; synchronized (this) { if (bs.currentVolatileBC(curBCI) == SLIM_QUICK) { arrayLoadNode = sparseNodes[curBCI]; @@ -1746,7 +1747,7 @@ private int quickenArrayLoad(VirtualFrame frame, long[] primitives, Object[] ref private int quickenArrayStore(final VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int storeOpcode, JavaKind componentKind) { CompilerDirectives.transferToInterpreterAndInvalidate(); - QuickNode arrayStoreNode; + EspressoInstrumentableQuickNode arrayStoreNode; synchronized (this) { if (bs.currentVolatileBC(curBCI) == SLIM_QUICK) { arrayStoreNode = sparseNodes[curBCI]; @@ -1777,9 +1778,9 @@ private int quickenArrayStore(final VirtualFrame frame, long[] primitives, Objec // endregion quickenForeign - private QuickNode dispatchQuickened(int top, int curBCI, char cpi, int opcode, int statementIndex, Method resolutionSeed, boolean allowFieldAccessInlining) { + private EspressoInstrumentableQuickNode dispatchQuickened(int top, int curBCI, char cpi, int opcode, int statementIndex, Method resolutionSeed, boolean allowFieldAccessInlining) { assert !allowFieldAccessInlining || getContext().InlineFieldAccessors; - QuickNode invoke; + EspressoInstrumentableQuickNode invoke; Method resolved = resolutionSeed; switch (opcode) { case INVOKESTATIC: @@ -1892,7 +1893,7 @@ private int quickenInvokeDynamic(final VirtualFrame frame, long[] primitives, Ob CompilerDirectives.transferToInterpreterAndInvalidate(); assert (Bytecodes.INVOKEDYNAMIC == opcode); RuntimeConstantPool pool = getConstantPool(); - QuickNode quick = null; + EspressoInstrumentableQuickNode quick = null; int indyIndex = -1; synchronized (this) { if (bs.currentVolatileBC(curBCI) == QUICK) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java index c1254ef7b180..bf7e2f20bad5 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java @@ -42,4 +42,10 @@ public final WrapperNode createWrapper(ProbeNode probeNode) { return new EspressoInstrumentableQuickNodeWrapper(this, probeNode); } + public abstract boolean producedForeignObject(Object[] refs); + + public boolean removedByRedefintion() { + return false; + } + } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java index e4a75b75b254..faf0515564c8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java @@ -134,14 +134,6 @@ public final boolean isBytecodeNode() { return getMethodNode() instanceof BytecodeNode; } - public final BytecodeNode getBytecodeNode() { - if (isBytecodeNode()) { - return (BytecodeNode) getMethodNode(); - } else { - return null; - } - } - public EspressoMethodNode getMethodNode() { Node child = methodNode; if (child instanceof WrapperNode) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java index 6b632ec85c89..7ce5edbcd3cd 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java @@ -56,12 +56,6 @@ protected QuickNode(int top, int callerBCI) { @Override public abstract int execute(VirtualFrame frame, long[] primitives, Object[] refs); - public boolean removedByRedefintion() { - return false; - } - - public abstract boolean producedForeignObject(Object[] refs); - protected final StaticObject nullCheck(StaticObject value) { if (StaticObject.isNull(value)) { enterExceptionProfile(); From c07573407c6ac5dbec77c0fdc4d36dffef456424 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Wed, 17 Mar 2021 15:32:42 +0100 Subject: [PATCH 039/290] Initial implementation of DynamicThresholdsQueue. --- .../options/PolyglotCompilerOptions.java | 3 + .../runtime/BackgroundCompileQueue.java | 12 ++- .../runtime/DynamicThresholdsQueue.java | 76 +++++++++++++++++++ .../truffle/runtime/GraalTruffleRuntime.java | 10 +++ .../truffle/runtime/OptimizedCallTarget.java | 10 +-- .../runtime/TraversingBlockingQueue.java | 2 +- 6 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index 95e208409aa2..af767326e936 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -516,6 +516,9 @@ private String indent(int nameLength) { @Option(help = "Traversing queue gives first tier compilations priority.", category = OptionCategory.INTERNAL) public static final OptionKey TraversingQueueFirstTierPriority = new OptionKey<>(true); + @Option(help = "TODO", category = OptionCategory.INTERNAL) + public static final OptionKey TraversingDynamicThresholds = new OptionKey<>(false); + // Language agnostic inlining @Option(help = "Print detailed information for inlining (i.e. the entire explored call tree).", category = OptionCategory.INTERNAL) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java index 85c514b3a5cd..10d45b9c4ac5 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java @@ -129,7 +129,7 @@ private ExecutorService getExecutorService(OptimizedCallTarget callTarget) { long compilerIdleDelay = runtime.getCompilerIdleDelay(callTarget); long keepAliveTime = compilerIdleDelay >= 0 ? compilerIdleDelay : 0; - initQueue(callTarget); + this.compilationQueue = createQueue(callTarget, threads); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(threads, threads, keepAliveTime, TimeUnit.MILLISECONDS, compilationQueue, factory) { @@ -151,11 +151,15 @@ protected RunnableFuture newTaskFor(Callable callable) { } } - private void initQueue(OptimizedCallTarget callTarget) { + private BlockingQueue createQueue(OptimizedCallTarget callTarget, int threads) { if (callTarget.getOptionValue(PolyglotCompilerOptions.TraversingCompilationQueue)) { - this.compilationQueue = new TraversingBlockingQueue(); + if (callTarget.getOptionValue(PolyglotCompilerOptions.TraversingDynamicThresholds)) { + return new DynamicThresholdsQueue(runtime, threads); + } else { + return new TraversingBlockingQueue(); + } } else { - this.compilationQueue = new IdlingPriorityBlockingQueue<>(); + return new IdlingPriorityBlockingQueue<>(); } } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java new file mode 100644 index 000000000000..03b4751c342f --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 org.graalvm.compiler.truffle.runtime; + +import java.util.concurrent.TimeUnit; + +public class DynamicThresholdsQueue extends TraversingBlockingQueue { + + public static final double ACTIVATION_TRIGGER = 3; + public static final double MINIMAL_SCALE = 0.25; + private final GraalTruffleRuntime runtime; + private final int threads; + + private boolean active; + + public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads) { + this.runtime = runtime; + this.threads = threads; + } + + private double load() { + return (double) entries.size() / threads; + } + + @Override + public boolean add(Runnable e) { + if (!active && load() > ACTIVATION_TRIGGER) { + active = true; + } + return super.add(e); + } + + @Override + public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { + if (active) { + scaleThresholds(); + } + return super.poll(timeout, unit); + } + + @Override + public Runnable poll() { + if (active) { + scaleThresholds(); + } + return super.poll(); + } + + private void scaleThresholds() { + double slope = (1 - MINIMAL_SCALE) / (ACTIVATION_TRIGGER - 1); + double intercept = 1 - slope * ACTIVATION_TRIGGER; + runtime.setCompilationThresholdScale(slope * load() + intercept); + } +} diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index eecae662a1ff..f49521f79aed 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -532,6 +532,16 @@ public T iterateFrames(final FrameInstanceVisitor visitor) { return iterateImpl(visitor, 0); } + private double scale = 1; + + double scale() { + return scale; + } + + void setCompilationThresholdScale(double scale) { + this.scale = scale; + } + private static final class FrameVisitor implements InspectedFrameVisitor { private final FrameInstanceVisitor visitor; diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java index ce6d68184539..bc23d70679f4 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java @@ -553,15 +553,15 @@ private boolean interpreterCall() { } private boolean shouldCompileImpl(int intCallCount, int intLoopCallCount) { - return intCallCount >= engine.callThresholdInInterpreter // - && intLoopCallCount >= engine.callAndLoopThresholdInInterpreter // - && !compilationFailed // - && !isSubmittedForCompilation() + return !compilationFailed // + && !isSubmittedForCompilation() // /* * Compilation of OSR loop call target is scheduled in * OptimizedOSRLoopNode#compileImpl. */ - && !(getRootNode() instanceof OSRRootNode); + && !(getRootNode() instanceof OSRRootNode) // + && intCallCount >= engine.callThresholdInInterpreter // + && intLoopCallCount >= engine.callAndLoopThresholdInInterpreter * runtime().scale(); // } public final boolean shouldCompile() { diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java index 0a741bf4443a..da4264c1b5ae 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java @@ -30,7 +30,7 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -public final class TraversingBlockingQueue implements BlockingQueue { +public class TraversingBlockingQueue implements BlockingQueue { final BlockingQueue entries = new LinkedBlockingDeque<>(); @SuppressWarnings("unchecked") From 3437972f5437058da8296a37b4949fa696fee055 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 18 Mar 2021 09:01:29 +0100 Subject: [PATCH 040/290] Separate notions of activation and normal load. --- .../runtime/BackgroundCompileQueue.java | 1 + .../runtime/DynamicThresholdsQueue.java | 28 +++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java index 10d45b9c4ac5..34426b74aeff 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java @@ -282,6 +282,7 @@ private final class TruffleCompilerThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { + System.out.println("@@ Creating thread!"); final Thread t = new Thread(r) { @SuppressWarnings("try") @Override diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 03b4751c342f..3bbb77d05940 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -28,8 +28,9 @@ public class DynamicThresholdsQueue extends TraversingBlockingQueue { - public static final double ACTIVATION_TRIGGER = 3; + public static final double ACTIVATION_TRIGGER = 1; public static final double MINIMAL_SCALE = 0.25; + public static final double NORMAL_LOAD = 2; private final GraalTruffleRuntime runtime; private final int threads; @@ -41,15 +42,31 @@ public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads) { } private double load() { - return (double) entries.size() / threads; + return ((double) entries.size() + 1) / threads; } @Override public boolean add(Runnable e) { + checkActive(); + return super.add(e); + } + + private void checkActive() { if (!active && load() > ACTIVATION_TRIGGER) { active = true; } - return super.add(e); + } + + @Override + public boolean offer(Runnable e) { + checkActive(); + return super.offer(e); + } + + @Override + public boolean offer(Runnable e, long timeout, TimeUnit unit) throws InterruptedException { + checkActive(); + return super.offer(e, timeout, unit); } @Override @@ -69,8 +86,7 @@ public Runnable poll() { } private void scaleThresholds() { - double slope = (1 - MINIMAL_SCALE) / (ACTIVATION_TRIGGER - 1); - double intercept = 1 - slope * ACTIVATION_TRIGGER; - runtime.setCompilationThresholdScale(slope * load() + intercept); + double slope = (1 - MINIMAL_SCALE) / NORMAL_LOAD; + runtime.setCompilationThresholdScale(slope * load() + MINIMAL_SCALE); } } From edac3c2ca91a9673486747426697b066d2a1486a Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 18 Mar 2021 10:53:27 +0100 Subject: [PATCH 041/290] Remove the notion of activation, i.e. start active. --- .../runtime/DynamicThresholdsQueue.java | 35 ++----------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 3bbb77d05940..e229cc45da04 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -28,14 +28,11 @@ public class DynamicThresholdsQueue extends TraversingBlockingQueue { - public static final double ACTIVATION_TRIGGER = 1; public static final double MINIMAL_SCALE = 0.25; public static final double NORMAL_LOAD = 2; private final GraalTruffleRuntime runtime; private final int threads; - private boolean active; - public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads) { this.runtime = runtime; this.threads = threads; @@ -45,43 +42,15 @@ private double load() { return ((double) entries.size() + 1) / threads; } - @Override - public boolean add(Runnable e) { - checkActive(); - return super.add(e); - } - - private void checkActive() { - if (!active && load() > ACTIVATION_TRIGGER) { - active = true; - } - } - - @Override - public boolean offer(Runnable e) { - checkActive(); - return super.offer(e); - } - - @Override - public boolean offer(Runnable e, long timeout, TimeUnit unit) throws InterruptedException { - checkActive(); - return super.offer(e, timeout, unit); - } - @Override public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { - if (active) { - scaleThresholds(); - } + scaleThresholds(); return super.poll(timeout, unit); } @Override public Runnable poll() { - if (active) { - scaleThresholds(); - } + scaleThresholds(); return super.poll(); } From 9eb219bae460bc876ea575290104dad29786c035 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 18 Mar 2021 11:36:59 +0100 Subject: [PATCH 042/290] Limit max scale to 1.0. --- .../compiler/truffle/runtime/DynamicThresholdsQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index e229cc45da04..bb89a3596534 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -56,6 +56,6 @@ public Runnable poll() { private void scaleThresholds() { double slope = (1 - MINIMAL_SCALE) / NORMAL_LOAD; - runtime.setCompilationThresholdScale(slope * load() + MINIMAL_SCALE); + runtime.setCompilationThresholdScale(Math.min(1.0, slope * load() + MINIMAL_SCALE)); } } From 1132895c0255f4cb6e077e3b1a166704268e9e2d Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 18 Mar 2021 12:25:43 +0100 Subject: [PATCH 043/290] Scale last tier compilation thresholds. --- .../compiler/truffle/runtime/OptimizedCallTarget.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java index bc23d70679f4..0d99e80df89e 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java @@ -593,10 +593,10 @@ public final boolean firstTierCall() { this.callCount = firstTierCallCount == Integer.MAX_VALUE ? firstTierCallCount : ++firstTierCallCount; int firstTierLoopCallCount = this.callAndLoopCount; this.callAndLoopCount = firstTierLoopCallCount == Integer.MAX_VALUE ? firstTierLoopCallCount : ++firstTierLoopCallCount; - if (firstTierCallCount >= engine.callThresholdInFirstTier // - && firstTierLoopCallCount >= engine.callAndLoopThresholdInFirstTier // - && !compilationFailed // - && !isSubmittedForCompilation()) { + if (!compilationFailed // + && !isSubmittedForCompilation()// + && firstTierCallCount >= engine.callThresholdInFirstTier // + && firstTierLoopCallCount >= engine.callAndLoopThresholdInFirstTier * runtime().scale()) { return lastTierCompile(); } return false; From 6f2462832210edb8a4445c934c6ed382bfd70056 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 18 Mar 2021 12:52:28 +0100 Subject: [PATCH 044/290] Set new values. --- .../compiler/truffle/runtime/DynamicThresholdsQueue.java | 6 +++--- .../compiler/truffle/runtime/GraalTruffleRuntime.java | 2 +- .../truffle/runtime/debug/TraceCompilationListener.java | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index bb89a3596534..63985f2da949 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -28,8 +28,8 @@ public class DynamicThresholdsQueue extends TraversingBlockingQueue { - public static final double MINIMAL_SCALE = 0.25; - public static final double NORMAL_LOAD = 2; + public static final double MINIMAL_SCALE = 0.01; + public static final double NORMAL_LOAD = 3; private final GraalTruffleRuntime runtime; private final int threads; @@ -39,7 +39,7 @@ public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads) { } private double load() { - return ((double) entries.size() + 1) / threads; + return (double) entries.size() / threads; } @Override diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index f49521f79aed..4347a3b7028d 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -534,7 +534,7 @@ public T iterateFrames(final FrameInstanceVisitor visitor) { private double scale = 1; - double scale() { + public double scale() { return scale; } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java index ccf4476ccbf3..c2dbbf536c35 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java @@ -92,6 +92,7 @@ private Map queueProperties(OptimizedCallTarget target, int tier properties.put("Src", formatSourceSection(target.getRootNode().getSourceSection())); properties.put("QueueSize", runtime.getCompilationQueueSize()); properties.put("Time", System.nanoTime() - startTime); + properties.put("Scale", runtime.scale()); return properties; } From f3e6ff552efaa38174a7642b4a1558ea8fd446f7 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 23 Mar 2021 13:39:50 +0100 Subject: [PATCH 045/290] Minimal scale and normal load as engine options. --- .../truffle/options/PolyglotCompilerOptions.java | 6 ++++++ .../truffle/runtime/BackgroundCompileQueue.java | 4 +++- .../truffle/runtime/DynamicThresholdsQueue.java | 12 +++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index af767326e936..bd6af635786b 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -519,6 +519,12 @@ private String indent(int nameLength) { @Option(help = "TODO", category = OptionCategory.INTERNAL) public static final OptionKey TraversingDynamicThresholds = new OptionKey<>(false); + @Option(help = "TODO", category = OptionCategory.INTERNAL) + public static final OptionKey DynamicThresholdMinScale = new OptionKey<>(0.1); + + @Option(help = "TODO", category = OptionCategory.INTERNAL) + public static final OptionKey DynamicThresholdNormLoad= new OptionKey<>(3); + // Language agnostic inlining @Option(help = "Print detailed information for inlining (i.e. the entire explored call tree).", category = OptionCategory.INTERNAL) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java index 34426b74aeff..0960a10ec6c4 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java @@ -154,7 +154,9 @@ protected RunnableFuture newTaskFor(Callable callable) { private BlockingQueue createQueue(OptimizedCallTarget callTarget, int threads) { if (callTarget.getOptionValue(PolyglotCompilerOptions.TraversingCompilationQueue)) { if (callTarget.getOptionValue(PolyglotCompilerOptions.TraversingDynamicThresholds)) { - return new DynamicThresholdsQueue(runtime, threads); + double minScale = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdMinScale); + int normalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdNormLoad); + return new DynamicThresholdsQueue(runtime, threads, minScale, normalLoad); } else { return new TraversingBlockingQueue(); } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 63985f2da949..7cd601636e9f 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -28,14 +28,16 @@ public class DynamicThresholdsQueue extends TraversingBlockingQueue { - public static final double MINIMAL_SCALE = 0.01; - public static final double NORMAL_LOAD = 3; private final GraalTruffleRuntime runtime; private final int threads; + private final double minScale; + private final int normalLoad; - public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads) { + public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads, double minScale, int normalLoad) { this.runtime = runtime; this.threads = threads; + this.minScale = minScale; + this.normalLoad = normalLoad; } private double load() { @@ -55,7 +57,7 @@ public Runnable poll() { } private void scaleThresholds() { - double slope = (1 - MINIMAL_SCALE) / NORMAL_LOAD; - runtime.setCompilationThresholdScale(Math.min(1.0, slope * load() + MINIMAL_SCALE)); + double slope = (1 - minScale) / normalLoad; + runtime.setCompilationThresholdScale(Math.min(1.0, slope * load() + minScale)); } } From dc1936db84f8dc221b3320c8d874bef42effb469 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 23 Mar 2021 15:54:18 +0100 Subject: [PATCH 046/290] Set thresholds on adding to queue as well. --- .../runtime/BackgroundCompileQueue.java | 1 - .../runtime/DynamicThresholdsQueue.java | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java index 0960a10ec6c4..f9bbe38f58f2 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java @@ -284,7 +284,6 @@ private final class TruffleCompilerThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { - System.out.println("@@ Creating thread!"); final Thread t = new Thread(r) { @SuppressWarnings("try") @Override diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 7cd601636e9f..cc5c8a88e0fd 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -44,6 +44,24 @@ private double load() { return (double) entries.size() / threads; } + @Override + public boolean add(Runnable e) { + scaleThresholds(); + return super.add(e); + } + + @Override + public boolean offer(Runnable e) { + scaleThresholds(); + return super.offer(e); + } + + @Override + public boolean offer(Runnable e, long timeout, TimeUnit unit) throws InterruptedException { + scaleThresholds(); + return super.offer(e, timeout, unit); + } + @Override public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { scaleThresholds(); From f9576bbc803e2aae6e34ba99fdf78c746b98ebbe Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 13 Apr 2021 11:25:18 +0200 Subject: [PATCH 047/290] Refactor: rename. --- .../compiler/truffle/options/PolyglotCompilerOptions.java | 2 +- .../compiler/truffle/runtime/GraalTruffleRuntime.java | 8 ++++---- .../compiler/truffle/runtime/OptimizedCallTarget.java | 4 ++-- .../truffle/runtime/debug/TraceCompilationListener.java | 7 ++++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index bd6af635786b..7d409cb85ea0 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -523,7 +523,7 @@ private String indent(int nameLength) { public static final OptionKey DynamicThresholdMinScale = new OptionKey<>(0.1); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholdNormLoad= new OptionKey<>(3); + public static final OptionKey DynamicThresholdNormLoad = new OptionKey<>(3); // Language agnostic inlining diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index 4347a3b7028d..24d3177d2f98 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -532,14 +532,14 @@ public T iterateFrames(final FrameInstanceVisitor visitor) { return iterateImpl(visitor, 0); } - private double scale = 1; + private volatile double compilationThresholdScale = 1.0; - public double scale() { - return scale; + public double compilationThresholdScale() { + return compilationThresholdScale; } void setCompilationThresholdScale(double scale) { - this.scale = scale; + this.compilationThresholdScale = scale; } private static final class FrameVisitor implements InspectedFrameVisitor { diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java index 0d99e80df89e..c8012df3b5f6 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java @@ -561,7 +561,7 @@ private boolean shouldCompileImpl(int intCallCount, int intLoopCallCount) { */ && !(getRootNode() instanceof OSRRootNode) // && intCallCount >= engine.callThresholdInInterpreter // - && intLoopCallCount >= engine.callAndLoopThresholdInInterpreter * runtime().scale(); // + && intLoopCallCount >= engine.callAndLoopThresholdInInterpreter * runtime().compilationThresholdScale(); // } public final boolean shouldCompile() { @@ -596,7 +596,7 @@ public final boolean firstTierCall() { if (!compilationFailed // && !isSubmittedForCompilation()// && firstTierCallCount >= engine.callThresholdInFirstTier // - && firstTierLoopCallCount >= engine.callAndLoopThresholdInFirstTier * runtime().scale()) { + && firstTierLoopCallCount >= engine.callAndLoopThresholdInFirstTier * runtime().compilationThresholdScale()) { return lastTierCompile(); } return false; diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java index c2dbbf536c35..514617590221 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java @@ -86,13 +86,14 @@ private Map queueProperties(OptimizedCallTarget target, int tier GraalTruffleRuntimeListener.addASTSizeProperty(target, properties); properties.put("Tier", Integer.toString(tier)); // to avoid padding int callThreshold = tier == 1 ? target.engine.callThresholdInInterpreter : target.engine.callThresholdInFirstTier; - properties.put("Calls/Thres", String.format("%7d/%5d", target.getCallCount(), callThreshold)); + double scale = runtime.compilationThresholdScale(); + properties.put("Calls/Thres", String.format("%7d/%5d", target.getCallCount(), (int) (scale * callThreshold))); int callAndLoopThreshold = tier == 1 ? target.engine.callAndLoopThresholdInInterpreter : target.engine.callAndLoopThresholdInFirstTier; - properties.put("CallsAndLoop/Thres", String.format("%7d/%5d", target.getCallAndLoopCount(), callAndLoopThreshold)); + properties.put("CallsAndLoop/Thres", String.format("%7d/%5d", target.getCallAndLoopCount(), (int) (scale * callAndLoopThreshold))); properties.put("Src", formatSourceSection(target.getRootNode().getSourceSection())); properties.put("QueueSize", runtime.getCompilationQueueSize()); properties.put("Time", System.nanoTime() - startTime); - properties.put("Scale", runtime.scale()); + properties.put("Scale", scale); return properties; } From c73c8530d6a107bdfab9a0fd291d2d1918b5e554 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 13 Apr 2021 14:40:06 +0200 Subject: [PATCH 048/290] Do not limit scale. --- .../compiler/truffle/runtime/DynamicThresholdsQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index cc5c8a88e0fd..ee44da1131af 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -76,6 +76,6 @@ public Runnable poll() { private void scaleThresholds() { double slope = (1 - minScale) / normalLoad; - runtime.setCompilationThresholdScale(Math.min(1.0, slope * load() + minScale)); + runtime.setCompilationThresholdScale(slope * load() + minScale); } } From 21ed6d2a830ea7d529134ca6c3f33623bcc9396a Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 15 Apr 2021 18:14:29 +0200 Subject: [PATCH 049/290] Plateau based scale function for dynamic thresholds. --- .../options/PolyglotCompilerOptions.java | 9 ++++++--- .../truffle/runtime/BackgroundCompileQueue.java | 9 +++++---- .../truffle/runtime/DynamicThresholdsQueue.java | 17 +++++++++++++++-- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index 7d409cb85ea0..cf0e08f26912 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -517,13 +517,16 @@ private String indent(int nameLength) { public static final OptionKey TraversingQueueFirstTierPriority = new OptionKey<>(true); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey TraversingDynamicThresholds = new OptionKey<>(false); + public static final OptionKey DynamicThresholds = new OptionKey<>(false); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholdMinScale = new OptionKey<>(0.1); + public static final OptionKey DynamicThresholdsMinScale = new OptionKey<>(0.1); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholdNormLoad = new OptionKey<>(3); + public static final OptionKey DynamicThresholdsNormalLoad = new OptionKey<>(50); + + @Option(help = "TODO", category = OptionCategory.INTERNAL) + public static final OptionKey DynamicThresholdPlateauWidth = new OptionKey<>(0.8); // Language agnostic inlining diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java index f9bbe38f58f2..1f666a2ec563 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java @@ -153,10 +153,11 @@ protected RunnableFuture newTaskFor(Callable callable) { private BlockingQueue createQueue(OptimizedCallTarget callTarget, int threads) { if (callTarget.getOptionValue(PolyglotCompilerOptions.TraversingCompilationQueue)) { - if (callTarget.getOptionValue(PolyglotCompilerOptions.TraversingDynamicThresholds)) { - double minScale = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdMinScale); - int normalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdNormLoad); - return new DynamicThresholdsQueue(runtime, threads, minScale, normalLoad); + if (callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholds)) { + double minScale = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdsMinScale); + int normalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdsNormalLoad); + double plateauWidth = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdPlateauWidth); + return new DynamicThresholdsQueue(runtime, threads, minScale, normalLoad, plateauWidth); } else { return new TraversingBlockingQueue(); } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index ee44da1131af..8890d728a67d 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -32,12 +32,14 @@ public class DynamicThresholdsQueue extends TraversingBlockingQueue { private final int threads; private final double minScale; private final int normalLoad; + private final double plateauWidth; - public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads, double minScale, int normalLoad) { + public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads, double minScale, int normalLoad, double plateauWidth) { this.runtime = runtime; this.threads = threads; this.minScale = minScale; this.normalLoad = normalLoad; + this.plateauWidth = plateauWidth; } private double load() { @@ -75,7 +77,18 @@ public Runnable poll() { } private void scaleThresholds() { + runtime.setCompilationThresholdScale(scale()); + } + + private double scale() { + double x = load(); + if ((1 - plateauWidth) * normalLoad <= x && x <= (1 + plateauWidth) * normalLoad) { + return 1; + } double slope = (1 - minScale) / normalLoad; - runtime.setCompilationThresholdScale(slope * load() + minScale); + if (x < (1 - plateauWidth) * normalLoad) { + return slope * x + minScale; + } + return slope * x - (1 + plateauWidth) * slope * normalLoad + 1; } } From bf7a49002298864e876ff0c143ed29ec3fa8807a Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 15 Apr 2021 18:32:28 +0200 Subject: [PATCH 050/290] Make compilationThresholdScale not volatile. --- .../graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index 24d3177d2f98..a019be4550d9 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -532,7 +532,7 @@ public T iterateFrames(final FrameInstanceVisitor visitor) { return iterateImpl(visitor, 0); } - private volatile double compilationThresholdScale = 1.0; + private double compilationThresholdScale = 1.0; public double compilationThresholdScale() { return compilationThresholdScale; From 708e4744b741fa56b64a398b5bbe9fb31fecbdbf Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Fri, 16 Apr 2021 11:42:22 +0200 Subject: [PATCH 051/290] Fix the slope calculation. --- .../compiler/truffle/runtime/DynamicThresholdsQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 8890d728a67d..a806535406c9 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -85,7 +85,7 @@ private double scale() { if ((1 - plateauWidth) * normalLoad <= x && x <= (1 + plateauWidth) * normalLoad) { return 1; } - double slope = (1 - minScale) / normalLoad; + double slope = (1 - minScale) / ((1-plateauWidth) * normalLoad); if (x < (1 - plateauWidth) * normalLoad) { return slope * x + minScale; } From 638ed7cd348f61ef469fafa9fe0a25f3f38a6f20 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Fri, 16 Apr 2021 14:57:30 +0200 Subject: [PATCH 052/290] New values for dynamic thresholds parameters. --- .../compiler/truffle/options/PolyglotCompilerOptions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index cf0e08f26912..b32bdcf3be6d 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -523,10 +523,10 @@ private String indent(int nameLength) { public static final OptionKey DynamicThresholdsMinScale = new OptionKey<>(0.1); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholdsNormalLoad = new OptionKey<>(50); + public static final OptionKey DynamicThresholdsNormalLoad = new OptionKey<>(100); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholdPlateauWidth = new OptionKey<>(0.8); + public static final OptionKey DynamicThresholdPlateauWidth = new OptionKey<>(0.9); // Language agnostic inlining From 6d38fc0353033e6b143bc22f509b4f62b61cb233 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 20 Apr 2021 10:45:09 +0200 Subject: [PATCH 053/290] Enable traversing queue and dynamic thresholds. --- .../compiler/truffle/options/PolyglotCompilerOptions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index b32bdcf3be6d..d6dc0fe73fad 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -508,7 +508,7 @@ private String indent(int nameLength) { public static final OptionKey PriorityQueue = new OptionKey<>(true); @Option(help = "Use a traversing compilation queue.", category = OptionCategory.INTERNAL) - public static final OptionKey TraversingCompilationQueue = new OptionKey<>(false); + public static final OptionKey TraversingCompilationQueue = new OptionKey<>(true); @Option(help = "Traversing queue uses rate as priority for both tier.", category = OptionCategory.INTERNAL) public static final OptionKey TraversingQueueWeightingBothTiers = new OptionKey<>(true); @@ -517,7 +517,7 @@ private String indent(int nameLength) { public static final OptionKey TraversingQueueFirstTierPriority = new OptionKey<>(true); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholds = new OptionKey<>(false); + public static final OptionKey DynamicThresholds = new OptionKey<>(true); @Option(help = "TODO", category = OptionCategory.INTERNAL) public static final OptionKey DynamicThresholdsMinScale = new OptionKey<>(0.1); From 6accd3acba04725c4d84dbdfd9f3a85d7c8c08d1 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Wed, 21 Apr 2021 10:34:45 +0200 Subject: [PATCH 054/290] Do not use dynamic thresholds in tests with synchronous compiling. --- .../compiler/truffle/test/TestWithSynchronousCompiling.java | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TestWithSynchronousCompiling.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TestWithSynchronousCompiling.java index 2154b6603386..fbde0e166fb3 100644 --- a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TestWithSynchronousCompiling.java +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TestWithSynchronousCompiling.java @@ -51,6 +51,7 @@ public abstract class TestWithSynchronousCompiling extends TestWithPolyglotOptio "engine.SingleTierCompilationThreshold", "10", // "engine.LastTierCompilationThreshold", "10", // "engine.FirstTierCompilationThreshold", "5", // + "engine.DynamicThresholds", Boolean.FALSE.toString(), // "engine.CompileImmediately", Boolean.FALSE.toString() }; From 8d281512ff83598e9690ded51bf901a628c62f9c Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Wed, 21 Apr 2021 11:03:43 +0200 Subject: [PATCH 055/290] Formatting. --- .../compiler/truffle/runtime/DynamicThresholdsQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index a806535406c9..0d3fe95e2fb3 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -85,7 +85,7 @@ private double scale() { if ((1 - plateauWidth) * normalLoad <= x && x <= (1 + plateauWidth) * normalLoad) { return 1; } - double slope = (1 - minScale) / ((1-plateauWidth) * normalLoad); + double slope = (1 - minScale) / ((1 - plateauWidth) * normalLoad); if (x < (1 - plateauWidth) * normalLoad) { return slope * x + minScale; } From 8255475bb4abd6c787a736d19be6e56c1adc98de Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Fri, 23 Apr 2021 08:43:44 +0200 Subject: [PATCH 056/290] Use take max for take as well. --- .../compiler/truffle/runtime/TraversingBlockingQueue.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java index da4264c1b5ae..5f953a23249b 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java @@ -142,6 +142,10 @@ public boolean offer(Runnable e, long timeout, TimeUnit unit) throws Interrupted @Override public Runnable take() throws InterruptedException { + Runnable max = takeMax(); + if (max != null) { + return max; + } return entries.take(); } From b6a28a09040609a8a3d6ab8055a05e765097ba30 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Mon, 26 Apr 2021 09:02:42 +0200 Subject: [PATCH 057/290] Return init jobs early. --- .../compiler/truffle/runtime/TraversingBlockingQueue.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java index 5f953a23249b..aed82da49516 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java @@ -85,8 +85,8 @@ private synchronized Runnable takeMax() { while (it.hasNext()) { Runnable entry = it.next(); if (!(entry instanceof CompilationTask.ExecutorServiceWrapper)) { - // Any non compilation task (e.g. init tasks) has priority - removeAndReturn(entry); + // Any non-compilation task (e.g. init tasks) has priority + return removeAndReturn(entry); } CompilationTask task = task(entry); // updateWeight returns a negative number only if the task's target does not exist @@ -101,7 +101,7 @@ private synchronized Runnable takeMax() { return removeAndReturn(max); } - private Runnable removeAndReturn(Runnable max) { + private synchronized Runnable removeAndReturn(Runnable max) { // entries.remove can only return false if a sleeping thread takes the only element if (entries.remove(max)) { return max; From 0818771cee882420ca2d15a8872be51cb0053fe9 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Mon, 26 Apr 2021 15:37:48 +0200 Subject: [PATCH 058/290] Do not remove canceled and GC-ed tasks. --- .../compiler/truffle/runtime/TraversingBlockingQueue.java | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java index aed82da49516..1a7a20c8c6c5 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java @@ -91,7 +91,6 @@ private synchronized Runnable takeMax() { CompilationTask task = task(entry); // updateWeight returns a negative number only if the task's target does not exist if (task.isCancelled() || task.updateWeight(time) < 0) { - it.remove(); continue; } if (max == null || task.isHigherPriorityThan(task(max))) { From 608346c670510141ca38a033b92257af8ecfe806 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 27 Apr 2021 14:41:23 +0200 Subject: [PATCH 059/290] Handle init tasks correctly. --- .../org/graalvm/compiler/truffle/runtime/CompilationTask.java | 4 ++++ .../compiler/truffle/runtime/TraversingBlockingQueue.java | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java index 0d3027859a75..3e727fde832c 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java @@ -197,6 +197,10 @@ public Void call() throws Exception { * corrupt a queue data structure. */ boolean isHigherPriorityThan(CompilationTask other) { + if (action != compilationAction) { + // Any non-compilation action (e.g. compiler init) is higher priority. + return true; + } int tier = tier(); if (engineData.traversingFirstTierPriority && tier != other.tier()) { return tier < other.tier(); diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java index 1a7a20c8c6c5..d8043a811364 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java @@ -84,10 +84,6 @@ private synchronized Runnable takeMax() { Runnable max = null; while (it.hasNext()) { Runnable entry = it.next(); - if (!(entry instanceof CompilationTask.ExecutorServiceWrapper)) { - // Any non-compilation task (e.g. init tasks) has priority - return removeAndReturn(entry); - } CompilationTask task = task(entry); // updateWeight returns a negative number only if the task's target does not exist if (task.isCancelled() || task.updateWeight(time) < 0) { From 380910e963cb31949cc5e8a34c79a4d3b6eaaefd Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 29 Apr 2021 09:31:16 +0200 Subject: [PATCH 060/290] Cast scaling result to int for faster comparison. --- .../graalvm/compiler/truffle/runtime/OptimizedCallTarget.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java index c8012df3b5f6..fa06ce166c45 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java @@ -561,7 +561,7 @@ private boolean shouldCompileImpl(int intCallCount, int intLoopCallCount) { */ && !(getRootNode() instanceof OSRRootNode) // && intCallCount >= engine.callThresholdInInterpreter // - && intLoopCallCount >= engine.callAndLoopThresholdInInterpreter * runtime().compilationThresholdScale(); // + && intLoopCallCount >= (int) (engine.callAndLoopThresholdInInterpreter * runtime().compilationThresholdScale()); // } public final boolean shouldCompile() { @@ -596,7 +596,7 @@ public final boolean firstTierCall() { if (!compilationFailed // && !isSubmittedForCompilation()// && firstTierCallCount >= engine.callThresholdInFirstTier // - && firstTierLoopCallCount >= engine.callAndLoopThresholdInFirstTier * runtime().compilationThresholdScale()) { + && firstTierLoopCallCount >= (int) (engine.callAndLoopThresholdInFirstTier * runtime().compilationThresholdScale())) { return lastTierCompile(); } return false; From befbbbf8f3a6d9be2f7424ef0f9cb6b184f84d04 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 29 Apr 2021 09:52:14 +0200 Subject: [PATCH 061/290] Rename options. --- .../compiler/truffle/options/PolyglotCompilerOptions.java | 8 ++++---- .../compiler/truffle/runtime/BackgroundCompileQueue.java | 8 ++++---- .../truffle/test/TestWithSynchronousCompiling.java | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index d6dc0fe73fad..fef663b7e52e 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -517,16 +517,16 @@ private String indent(int nameLength) { public static final OptionKey TraversingQueueFirstTierPriority = new OptionKey<>(true); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholds = new OptionKey<>(true); + public static final OptionKey DynamicCompilationThresholds = new OptionKey<>(true); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholdsMinScale = new OptionKey<>(0.1); + public static final OptionKey DynamicCompilerThresholdsMinScale = new OptionKey<>(0.1); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholdsNormalLoad = new OptionKey<>(100); + public static final OptionKey DynamicCompilerThresholdsNormalLoad = new OptionKey<>(100); @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicThresholdPlateauWidth = new OptionKey<>(0.9); + public static final OptionKey DynamicCompilationThresholdsPlateauWidth = new OptionKey<>(0.9); // Language agnostic inlining diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java index 1f666a2ec563..bff32b59868c 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java @@ -153,10 +153,10 @@ protected RunnableFuture newTaskFor(Callable callable) { private BlockingQueue createQueue(OptimizedCallTarget callTarget, int threads) { if (callTarget.getOptionValue(PolyglotCompilerOptions.TraversingCompilationQueue)) { - if (callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholds)) { - double minScale = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdsMinScale); - int normalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdsNormalLoad); - double plateauWidth = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicThresholdPlateauWidth); + if (callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilationThresholds)) { + double minScale = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsMinScale); + int normalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsNormalLoad); + double plateauWidth = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilationThresholdsPlateauWidth); return new DynamicThresholdsQueue(runtime, threads, minScale, normalLoad, plateauWidth); } else { return new TraversingBlockingQueue(); diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TestWithSynchronousCompiling.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TestWithSynchronousCompiling.java index fbde0e166fb3..f2181a020bc3 100644 --- a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TestWithSynchronousCompiling.java +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TestWithSynchronousCompiling.java @@ -51,7 +51,7 @@ public abstract class TestWithSynchronousCompiling extends TestWithPolyglotOptio "engine.SingleTierCompilationThreshold", "10", // "engine.LastTierCompilationThreshold", "10", // "engine.FirstTierCompilationThreshold", "5", // - "engine.DynamicThresholds", Boolean.FALSE.toString(), // + "engine.DynamicCompilationThresholds", Boolean.FALSE.toString(), // "engine.CompileImmediately", Boolean.FALSE.toString() }; From a840561173c4d3c24656cc3e1b8706a5d7f13dc6 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 29 Apr 2021 11:50:03 +0200 Subject: [PATCH 062/290] Disable dynamic compilation thresholds for testing. --- compiler/mx.compiler/mx_compiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/mx.compiler/mx_compiler.py b/compiler/mx.compiler/mx_compiler.py index 82ac04b77fd4..6948b82e228c 100644 --- a/compiler/mx.compiler/mx_compiler.py +++ b/compiler/mx.compiler/mx_compiler.py @@ -694,6 +694,8 @@ def _unittest_config_participant(config): if _get_XX_option_value(vmArgs, 'TypeProfileWidth', None) is None: vmArgs.append('-XX:TypeProfileWidth=8') + vmArgs.append('-Dpolyglot.engine.DynamicCompilationThresholds=false') + vmArgs.append('-Dpolyglot.engine.AllowExperimentalOptions=true') return (vmArgs, mainClass, mainClassArgs) mx_unittest.add_config_participant(_unittest_config_participant) From e330a077a25d89e0bf6623a6a89224e63642b7b1 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Mon, 3 May 2021 10:01:46 +0200 Subject: [PATCH 063/290] Refactor: Inline method and change return value. --- .../compiler/truffle/runtime/CompilationTask.java | 11 +++++++---- .../truffle/runtime/TraversingBlockingQueue.java | 14 +++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java index 3e727fde832c..fa288f4e897f 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java @@ -217,14 +217,17 @@ boolean isHigherPriorityThan(CompilationTask other) { return false; } - double updateWeight(long currentTime) { + /** + * @return false if the target reference is null (i.e. if the target was garbage-collected. + */ + boolean updateWeight(long currentTime) { OptimizedCallTarget target = targetRef.get(); if (target == null) { - return -1.0; + return false; } long elapsed = currentTime - lastTime; if (elapsed < 1_000_000) { - return lastWeight; + return true; } int count = target.getCallAndLoopCount(); double weight = rate(count, elapsed) * count; @@ -232,7 +235,7 @@ boolean isHigherPriorityThan(CompilationTask other) { lastCount = count; lastWeight = weight; assert weight >= 0.0 : "weight must be positive"; - return weight; + return true; } private double rate(int count, long elapsed) { diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java index d8043a811364..cb5aad42ff4e 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java @@ -85,24 +85,16 @@ private synchronized Runnable takeMax() { while (it.hasNext()) { Runnable entry = it.next(); CompilationTask task = task(entry); - // updateWeight returns a negative number only if the task's target does not exist - if (task.isCancelled() || task.updateWeight(time) < 0) { + // updateWeight returns false only if the task's target does not exist + if (task.isCancelled() || !task.updateWeight(time)) { continue; } if (max == null || task.isHigherPriorityThan(task(max))) { max = entry; } } - return removeAndReturn(max); - } - - private synchronized Runnable removeAndReturn(Runnable max) { // entries.remove can only return false if a sleeping thread takes the only element - if (entries.remove(max)) { - return max; - } else { - return null; - } + return entries.remove(max) ? max : null; } @Override From 66ab2a544a37230ec3e6ab036d9b48ca8b32cb93 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Mon, 3 May 2021 10:47:22 +0200 Subject: [PATCH 064/290] Use fixed point arithmetic to speed up scale calculation. --- .../runtime/DynamicThresholdsQueue.java | 2 +- .../truffle/runtime/FixedPointMath.java | 44 +++++++++++++++++++ .../truffle/runtime/GraalTruffleRuntime.java | 10 +++-- .../truffle/runtime/OptimizedCallTarget.java | 8 +++- 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/FixedPointMath.java diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 0d3fe95e2fb3..19300e0ee637 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -77,7 +77,7 @@ public Runnable poll() { } private void scaleThresholds() { - runtime.setCompilationThresholdScale(scale()); + runtime.setCompilationThresholdScale(FixedPointMath.toFixedPoint(scale())); } private double scale() { diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/FixedPointMath.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/FixedPointMath.java new file mode 100644 index 000000000000..6951bc3d03b2 --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/FixedPointMath.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 org.graalvm.compiler.truffle.runtime; + +final class FixedPointMath { + private static final int SCALE = 4; + + /** + * should not be instantiated. + */ + private FixedPointMath() { + throw new IllegalStateException("Should not instantiate" + this.getClass().getName()); + } + + static int toFixedPoint(double x) { + return (int) (x * (1 << SCALE)); + } + + static int multiply(int fixedPointValue, int anInteger) { + return (fixedPointValue * anInteger) >> SCALE; + } +} diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index a019be4550d9..2e4e7a9fcd93 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -532,13 +532,17 @@ public T iterateFrames(final FrameInstanceVisitor visitor) { return iterateImpl(visitor, 0); } - private double compilationThresholdScale = 1.0; + /** + * The compilation threshold scale is a real number. We use an integer which we treat as a fixed + * point value for performance reasons. + */ + private int compilationThresholdScale = FixedPointMath.toFixedPoint(1.0); - public double compilationThresholdScale() { + public int compilationThresholdScale() { return compilationThresholdScale; } - void setCompilationThresholdScale(double scale) { + void setCompilationThresholdScale(int scale) { this.compilationThresholdScale = scale; } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java index fa06ce166c45..5cdaa987ce5e 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java @@ -561,7 +561,11 @@ private boolean shouldCompileImpl(int intCallCount, int intLoopCallCount) { */ && !(getRootNode() instanceof OSRRootNode) // && intCallCount >= engine.callThresholdInInterpreter // - && intLoopCallCount >= (int) (engine.callAndLoopThresholdInInterpreter * runtime().compilationThresholdScale()); // + && intLoopCallCount >= scaledThreshold(engine.callAndLoopThresholdInInterpreter); // + } + + private static int scaledThreshold(int callAndLoopThresholdInInterpreter) { + return FixedPointMath.multiply(runtime().compilationThresholdScale(), callAndLoopThresholdInInterpreter); } public final boolean shouldCompile() { @@ -596,7 +600,7 @@ public final boolean firstTierCall() { if (!compilationFailed // && !isSubmittedForCompilation()// && firstTierCallCount >= engine.callThresholdInFirstTier // - && firstTierLoopCallCount >= (int) (engine.callAndLoopThresholdInFirstTier * runtime().compilationThresholdScale())) { + && firstTierLoopCallCount >= scaledThreshold(engine.callAndLoopThresholdInFirstTier)) { return lastTierCompile(); } return false; From aeb0697d32972fa9e2d282ef6e1a07a1f5a80f8f Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 3 May 2021 10:53:00 +0200 Subject: [PATCH 065/290] Introduce BaseQuickNode for improved seperation of concerns between instrumentation and BytecodeNode --- .../truffle/espresso/nodes/BytecodeNode.java | 41 +++++++++--------- .../EspressoInstrumentableQuickNode.java | 17 +------- .../espresso/nodes/quick/BaseQuickNode.java | 42 +++++++++++++++++++ .../espresso/nodes/quick/QuickNode.java | 5 +-- 4 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index ea64dc164822..2d874e189463 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -298,6 +298,7 @@ import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.nodes.helper.EspressoReferenceArrayStoreNode; +import com.oracle.truffle.espresso.nodes.quick.BaseQuickNode; import com.oracle.truffle.espresso.nodes.quick.CheckCastNode; import com.oracle.truffle.espresso.nodes.quick.InstanceOfNode; import com.oracle.truffle.espresso.nodes.quick.QuickNode; @@ -370,8 +371,8 @@ public final class BytecodeNode extends EspressoMethodNode { } // must not be of type QuickNode as it might be wrapped by instrumentation - @Children private EspressoInstrumentableQuickNode[] nodes = QuickNode.EMPTY_ARRAY; - @Children private EspressoInstrumentableQuickNode[] sparseNodes = QuickNode.EMPTY_ARRAY; + @Children private BaseQuickNode[] nodes = QuickNode.EMPTY_ARRAY; + @Children private BaseQuickNode[] sparseNodes = QuickNode.EMPTY_ARRAY; /** * Ideally, we would want one such node per AASTORE bytecode. Unfortunately, the AASTORE * bytecode is a single byte long, so we cannot quicken it, and it is far too common to pay for @@ -1080,7 +1081,7 @@ Object executeBody(VirtualFrame frame) { CompilerDirectives.transferToInterpreterAndInvalidate(); continue loop; } - EspressoInstrumentableQuickNode quickNode = nodes[readCPI(curBCI)]; + BaseQuickNode quickNode = nodes[readCPI(curBCI)]; if (quickNode.removedByRedefintion()) { CompilerDirectives.transferToInterpreterAndInvalidate(); synchronized (this) { @@ -1213,7 +1214,7 @@ Object executeBody(VirtualFrame frame) { } // This check includes newly rewritten QUICK nodes, not just curOpcode == quick if (noForeignObjects.isValid() && (bs.currentBC(curBCI) == QUICK || bs.currentBC(curBCI) == SLIM_QUICK)) { - EspressoInstrumentableQuickNode quickNode; + BaseQuickNode quickNode; if (bs.currentBC(curBCI) == QUICK) { quickNode = nodes[readCPI(curBCI)]; } else { @@ -1569,7 +1570,7 @@ private char readOriginalCPI(int curBCI) { return BytecodeStream.readCPI(getMethodVersion().getOriginalCode(), curBCI); } - private char addQuickNode(EspressoInstrumentableQuickNode node) { + private char addQuickNode(BaseQuickNode node) { CompilerAsserts.neverPartOfCompilation(); Objects.requireNonNull(node); nodes = Arrays.copyOf(nodes, nodes.length + 1); @@ -1578,7 +1579,7 @@ private char addQuickNode(EspressoInstrumentableQuickNode node) { return (char) nodeIndex; } - private void addSlimQuickNode(EspressoInstrumentableQuickNode node, int curBCI) { + private void addSlimQuickNode(BaseQuickNode node, int curBCI) { CompilerAsserts.neverPartOfCompilation(); Objects.requireNonNull(node); if (sparseNodes == QuickNode.EMPTY_ARRAY) { @@ -1606,7 +1607,7 @@ private void patchBci(int bci, byte opcode, char nodeIndex) { VolatileArrayAccess.volatileWrite(code, bci, opcode); } - private EspressoInstrumentableQuickNode injectQuick(int curBCI, EspressoInstrumentableQuickNode quick, int opcode) { + private BaseQuickNode injectQuick(int curBCI, BaseQuickNode quick, int opcode) { QUICKENED_BYTECODES.inc(); CompilerAsserts.neverPartOfCompilation(); if (opcode == SLIM_QUICK) { @@ -1619,7 +1620,7 @@ private EspressoInstrumentableQuickNode injectQuick(int curBCI, EspressoInstrume return quick; } - private EspressoInstrumentableQuickNode tryPatchQuick(int curBCI, Supplier newQuickNode) { + private BaseQuickNode tryPatchQuick(int curBCI, Supplier newQuickNode) { synchronized (this) { if (bs.currentVolatileBC(curBCI) == QUICK) { return nodes[readCPI(curBCI)]; @@ -1636,7 +1637,7 @@ private int quickenCheckCast(VirtualFrame frame, long[] primitives, Object[] ref } CompilerDirectives.transferToInterpreterAndInvalidate(); assert opcode == CHECKCAST; - EspressoInstrumentableQuickNode quick = tryPatchQuick(curBCI, () -> new CheckCastNode(resolveType(CHECKCAST, readCPI(curBCI)), top, curBCI)); + BaseQuickNode quick = tryPatchQuick(curBCI, () -> new CheckCastNode(resolveType(CHECKCAST, readCPI(curBCI)), top, curBCI)); return quick.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(opcode); } @@ -1648,7 +1649,7 @@ private int quickenInstanceOf(VirtualFrame frame, long[] primitives, Object[] re } CompilerDirectives.transferToInterpreterAndInvalidate(); assert opcode == INSTANCEOF; - EspressoInstrumentableQuickNode quick = tryPatchQuick(curBCI, () -> new InstanceOfNode(resolveType(CHECKCAST, readCPI(curBCI)), top, curBCI)); + BaseQuickNode quick = tryPatchQuick(curBCI, () -> new InstanceOfNode(resolveType(CHECKCAST, readCPI(curBCI)), top, curBCI)); return quick.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(opcode); } @@ -1656,7 +1657,7 @@ private int quickenInvoke(VirtualFrame frame, long[] primitives, Object[] refs, QUICKENED_INVOKES.inc(); CompilerDirectives.transferToInterpreterAndInvalidate(); assert Bytecodes.isInvoke(opcode); - EspressoInstrumentableQuickNode quick = tryPatchQuick(curBCI, () -> { + BaseQuickNode quick = tryPatchQuick(curBCI, () -> { // During resolution of the symbolic reference to the method, any of the exceptions // pertaining to method resolution (§5.4.3.3) can be thrown. char cpi = readCPI(curBCI); @@ -1674,7 +1675,7 @@ private int quickenInvoke(VirtualFrame frame, long[] primitives, Object[] refs, public int reQuickenInvoke(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int opcode, int statementIndex, Method resolutionSeed) { CompilerDirectives.transferToInterpreterAndInvalidate(); assert Bytecodes.isInvoke(opcode); - EspressoInstrumentableQuickNode invoke = null; + BaseQuickNode invoke = null; synchronized (this) { assert bs.currentBC(curBCI) == QUICK; char cpi = readCPI(curBCI); @@ -1690,20 +1691,20 @@ public int reQuickenInvoke(VirtualFrame frame, long[] primitives, Object[] refs, public int quickenGetField(final VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int opcode, int statementIndex, Field field) { CompilerDirectives.transferToInterpreterAndInvalidate(); assert opcode == GETFIELD; - EspressoInstrumentableQuickNode getField = tryPatchQuick(curBCI, () -> new QuickenedGetFieldNode(top, curBCI, statementIndex, field)); + BaseQuickNode getField = tryPatchQuick(curBCI, () -> new QuickenedGetFieldNode(top, curBCI, statementIndex, field)); return getField.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(opcode); } public int quickenPutField(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int opcode, int statementIndex, Field field) { CompilerDirectives.transferToInterpreterAndInvalidate(); assert opcode == PUTFIELD; - EspressoInstrumentableQuickNode putField = tryPatchQuick(curBCI, () -> new QuickenedPutFieldNode(top, curBCI, field, statementIndex)); + BaseQuickNode putField = tryPatchQuick(curBCI, () -> new QuickenedPutFieldNode(top, curBCI, field, statementIndex)); return putField.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(opcode); } private int quickenArrayLength(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI) { CompilerDirectives.transferToInterpreterAndInvalidate(); - EspressoInstrumentableQuickNode arrayLengthNode; + BaseQuickNode arrayLengthNode; synchronized (this) { if (bs.currentVolatileBC(curBCI) == SLIM_QUICK) { arrayLengthNode = sparseNodes[curBCI]; @@ -1716,7 +1717,7 @@ private int quickenArrayLength(VirtualFrame frame, long[] primitives, Object[] r private int quickenArrayLoad(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int loadOpcode, JavaKind componentKind) { CompilerDirectives.transferToInterpreterAndInvalidate(); - EspressoInstrumentableQuickNode arrayLoadNode; + BaseQuickNode arrayLoadNode; synchronized (this) { if (bs.currentVolatileBC(curBCI) == SLIM_QUICK) { arrayLoadNode = sparseNodes[curBCI]; @@ -1747,7 +1748,7 @@ private int quickenArrayLoad(VirtualFrame frame, long[] primitives, Object[] ref private int quickenArrayStore(final VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int storeOpcode, JavaKind componentKind) { CompilerDirectives.transferToInterpreterAndInvalidate(); - EspressoInstrumentableQuickNode arrayStoreNode; + BaseQuickNode arrayStoreNode; synchronized (this) { if (bs.currentVolatileBC(curBCI) == SLIM_QUICK) { arrayStoreNode = sparseNodes[curBCI]; @@ -1778,9 +1779,9 @@ private int quickenArrayStore(final VirtualFrame frame, long[] primitives, Objec // endregion quickenForeign - private EspressoInstrumentableQuickNode dispatchQuickened(int top, int curBCI, char cpi, int opcode, int statementIndex, Method resolutionSeed, boolean allowFieldAccessInlining) { + private BaseQuickNode dispatchQuickened(int top, int curBCI, char cpi, int opcode, int statementIndex, Method resolutionSeed, boolean allowFieldAccessInlining) { assert !allowFieldAccessInlining || getContext().InlineFieldAccessors; - EspressoInstrumentableQuickNode invoke; + BaseQuickNode invoke; Method resolved = resolutionSeed; switch (opcode) { case INVOKESTATIC: @@ -1893,7 +1894,7 @@ private int quickenInvokeDynamic(final VirtualFrame frame, long[] primitives, Ob CompilerDirectives.transferToInterpreterAndInvalidate(); assert (Bytecodes.INVOKEDYNAMIC == opcode); RuntimeConstantPool pool = getConstantPool(); - EspressoInstrumentableQuickNode quick = null; + BaseQuickNode quick = null; int indyIndex = -1; synchronized (this) { if (bs.currentVolatileBC(curBCI) == QUICK) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java index bf7e2f20bad5..bc806e9814bd 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, 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 @@ -23,12 +23,9 @@ package com.oracle.truffle.espresso.nodes; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.InstrumentableNode; -import com.oracle.truffle.api.instrumentation.ProbeNode; import com.oracle.truffle.api.nodes.Node; -@GenerateWrapper public abstract class EspressoInstrumentableQuickNode extends Node implements InstrumentableNode { public abstract int execute(VirtualFrame frame, long[] primitives, Object[] refs); @@ -36,16 +33,4 @@ public abstract class EspressoInstrumentableQuickNode extends Node implements In public final boolean isInstrumentable() { return true; } - - @Override - public final WrapperNode createWrapper(ProbeNode probeNode) { - return new EspressoInstrumentableQuickNodeWrapper(this, probeNode); - } - - public abstract boolean producedForeignObject(Object[] refs); - - public boolean removedByRedefintion() { - return false; - } - } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java new file mode 100644 index 000000000000..118ad336fd90 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021, 2021, 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.nodes.quick; + +import com.oracle.truffle.api.instrumentation.GenerateWrapper; +import com.oracle.truffle.api.instrumentation.ProbeNode; +import com.oracle.truffle.espresso.nodes.EspressoInstrumentableQuickNode; + +@GenerateWrapper +public abstract class BaseQuickNode extends EspressoInstrumentableQuickNode { + + @Override + public final WrapperNode createWrapper(ProbeNode probeNode) { + return new BaseQuickNodeWrapper(this, probeNode); + } + + public abstract boolean producedForeignObject(Object[] refs); + + public boolean removedByRedefintion() { + return false; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java index 7ce5edbcd3cd..fd583ce221c0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, 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 @@ -27,10 +27,9 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.espresso.nodes.BytecodeNode; -import com.oracle.truffle.espresso.nodes.EspressoInstrumentableQuickNode; import com.oracle.truffle.espresso.runtime.StaticObject; -public abstract class QuickNode extends EspressoInstrumentableQuickNode { +public abstract class QuickNode extends BaseQuickNode { public static final QuickNode[] EMPTY_ARRAY = new QuickNode[0]; From d94d7b74aa4b5099c5414759dd70b75317c88838 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 3 May 2021 12:38:33 +0200 Subject: [PATCH 066/290] get rid of EspressoInstrumentableQuickNode --- .../EspressoInstrumentableQuickNode.java | 36 ------------------- .../espresso/nodes/quick/BaseQuickNode.java | 12 +++++-- 2 files changed, 10 insertions(+), 38 deletions(-) delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java deleted file mode 100644 index bc806e9814bd..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableQuickNode.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 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.nodes; - -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrumentation.InstrumentableNode; -import com.oracle.truffle.api.nodes.Node; - -public abstract class EspressoInstrumentableQuickNode extends Node implements InstrumentableNode { - - public abstract int execute(VirtualFrame frame, long[] primitives, Object[] refs); - - public final boolean isInstrumentable() { - return true; - } -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java index 118ad336fd90..ceaba3cf065c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java @@ -22,12 +22,20 @@ */ package com.oracle.truffle.espresso.nodes.quick; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.GenerateWrapper; +import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.instrumentation.ProbeNode; -import com.oracle.truffle.espresso.nodes.EspressoInstrumentableQuickNode; +import com.oracle.truffle.api.nodes.Node; @GenerateWrapper -public abstract class BaseQuickNode extends EspressoInstrumentableQuickNode { +public abstract class BaseQuickNode extends Node implements InstrumentableNode { + + public abstract int execute(VirtualFrame frame, long[] primitives, Object[] refs); + + public final boolean isInstrumentable() { + return true; + } @Override public final WrapperNode createWrapper(ProbeNode probeNode) { From 70a3f05db0fed565655aa92a9ec8cefcea377406 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 4 May 2021 11:52:35 +0200 Subject: [PATCH 067/290] simplify EspressoRootNode by removing the need for instrumentation knowledge about the methodNode child node --- ...eNode.java => EspressoBaseMethodNode.java} | 11 +++++-- .../espresso/nodes/EspressoMethodNode.java | 4 ++- .../espresso/nodes/EspressoRootNode.java | 32 ++++--------------- .../nodes/IntrinsicSubstitutorNode.java | 2 +- .../espresso/nodes/NativeMethodNode.java | 2 +- .../espresso/nodes/quick/BaseQuickNode.java | 2 ++ .../espresso/nodes/quick/QuickNode.java | 1 + .../espresso/runtime/JDWPContextImpl.java | 27 ++++++++++------ 8 files changed, 40 insertions(+), 41 deletions(-) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/{EspressoPreludeNode.java => EspressoBaseMethodNode.java} (80%) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoPreludeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoBaseMethodNode.java similarity index 80% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoPreludeNode.java rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoBaseMethodNode.java index 143c35b057de..8171ee4375cc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoPreludeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoBaseMethodNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -25,13 +25,18 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.ProbeNode; +import com.oracle.truffle.espresso.impl.Method; @GenerateWrapper -abstract class EspressoPreludeNode extends EspressoInstrumentableNode { +public abstract class EspressoBaseMethodNode extends EspressoInstrumentableNode { abstract Object executeBody(VirtualFrame frame); abstract void initializeBody(VirtualFrame frame); + public abstract Method.MethodVersion getMethodVersion(); + + public abstract boolean shouldSplit(); + @Override public final Object execute(VirtualFrame frame) { initializeBody(frame); @@ -40,6 +45,6 @@ public final Object execute(VirtualFrame frame) { @Override public WrapperNode createWrapper(ProbeNode probeNode) { - return new EspressoPreludeNodeWrapper(this, probeNode); + return new EspressoBaseMethodNodeWrapper(this, probeNode); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoMethodNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoMethodNode.java index 806dd5232943..0e2b539bec03 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoMethodNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoMethodNode.java @@ -38,7 +38,7 @@ /** * Base node for all implementations of Java methods. */ -public abstract class EspressoMethodNode extends EspressoPreludeNode { +public abstract class EspressoMethodNode extends EspressoBaseMethodNode { private final MethodVersion method; private SourceSection sourceSection; @@ -47,6 +47,7 @@ public abstract class EspressoMethodNode extends EspressoPreludeNode { this.method = method; } + @Override public MethodVersion getMethodVersion() { return method; } @@ -113,6 +114,7 @@ public final EspressoContext getContext() { return getMethod().getContext(); } + @Override public boolean shouldSplit() { return false; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java index faf0515564c8..ed0364f9b6a4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java @@ -22,9 +22,6 @@ */ package com.oracle.truffle.espresso.nodes; -import static com.oracle.truffle.espresso.vm.VM.StackElement.NATIVE_BCI; -import static com.oracle.truffle.espresso.vm.VM.StackElement.UNKNOWN_BCI; - import java.util.Arrays; import com.oracle.truffle.api.frame.Frame; @@ -34,8 +31,6 @@ import com.oracle.truffle.api.frame.FrameSlotTypeException; import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode; -import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.source.SourceSection; @@ -55,7 +50,7 @@ public abstract class EspressoRootNode extends RootNode implements ContextAccess { // must not be of type EspressoMethodNode as it might be wrapped by instrumentation - @Child protected EspressoInstrumentableNode methodNode; + @Child protected EspressoBaseMethodNode methodNode; private final FrameSlot monitorSlot; /** @@ -66,14 +61,14 @@ public abstract class EspressoRootNode extends RootNode implements ContextAccess private final BranchProfile unbalancedMonitorProfile = BranchProfile.create(); - EspressoRootNode(FrameDescriptor frameDescriptor, EspressoMethodNode methodNode, boolean usesMonitors) { + EspressoRootNode(FrameDescriptor frameDescriptor, EspressoBaseMethodNode methodNode, boolean usesMonitors) { super(methodNode.getMethod().getEspressoLanguage(), frameDescriptor); this.methodNode = methodNode; this.monitorSlot = usesMonitors ? frameDescriptor.addFrameSlot("monitor", FrameSlotKind.Object) : null; this.cookieSlot = frameDescriptor.addFrameSlot("cookie", FrameSlotKind.Object); } - private EspressoRootNode(EspressoRootNode split, FrameDescriptor frameDescriptor, EspressoMethodNode methodNode) { + private EspressoRootNode(EspressoRootNode split, FrameDescriptor frameDescriptor, EspressoBaseMethodNode methodNode) { super(methodNode.getMethod().getEspressoLanguage(), frameDescriptor); this.methodNode = methodNode; this.monitorSlot = split.monitorSlot; @@ -130,17 +125,8 @@ public final SourceSection getEncapsulatingSourceSection() { return getMethodNode().getEncapsulatingSourceSection(); } - public final boolean isBytecodeNode() { - return getMethodNode() instanceof BytecodeNode; - } - - public EspressoMethodNode getMethodNode() { - Node child = methodNode; - if (child instanceof WrapperNode) { - child = ((WrapperNode) child).getDelegateNode(); - } - assert !(child instanceof WrapperNode); - return (EspressoMethodNode) child; + public EspressoBaseMethodNode getMethodNode() { + return methodNode; } public static EspressoRootNode create(FrameDescriptor descriptor, EspressoMethodNode methodNode) { @@ -153,13 +139,7 @@ public static EspressoRootNode create(FrameDescriptor descriptor, EspressoMethod } public final int readBCI(Frame frame) { - if (isBytecodeNode()) { - return getMethodNode().getCurrentBCI(frame); - } else if (getMethod().isNative()) { - return NATIVE_BCI; // native - } else { - return UNKNOWN_BCI; // unknown - } + return getMethodNode().getCurrentBCI(frame); } public final void setFrameId(Frame frame, long frameId) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java index a527294b46f6..59c9374ed977 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java @@ -97,6 +97,6 @@ public Node copy() { @Override public int getCurrentBCI(@SuppressWarnings("unused") Frame frame) { - return -1; + return -2; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java index 94f1ca7f6a3c..77d09958c51d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java @@ -145,6 +145,6 @@ protected Object processResult(JniEnv env, Object result) { @Override public int getCurrentBCI(@SuppressWarnings("unused") Frame frame) { - return -1; + return -2; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java index ceaba3cf065c..9ac2d4050366 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java @@ -47,4 +47,6 @@ public final WrapperNode createWrapper(ProbeNode probeNode) { public boolean removedByRedefintion() { return false; } + + public abstract Node getBytecodesNode(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java index fd583ce221c0..46a1e7cee922 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java @@ -63,6 +63,7 @@ protected final StaticObject nullCheck(StaticObject value) { return value; } + @Override public final BytecodeNode getBytecodesNode() { return (BytecodeNode) getParent(); } 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 b4ed2605f264..340a624282a5 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 @@ -34,6 +34,7 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; @@ -70,7 +71,7 @@ import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.nodes.EspressoInstrumentableNode; import com.oracle.truffle.espresso.nodes.EspressoRootNode; -import com.oracle.truffle.espresso.nodes.quick.QuickNode; +import com.oracle.truffle.espresso.nodes.quick.BaseQuickNode; import com.oracle.truffle.espresso.nodes.quick.interop.ForeignArrayUtils; import com.oracle.truffle.espresso.runtime.dispatch.EspressoInterop; import com.oracle.truffle.espresso.substitutions.Target_java_lang_Thread; @@ -598,9 +599,7 @@ public int getNextBCI(RootNode callerRoot, Frame frame) { public long readBCIFromFrame(RootNode root, Frame frame) { if (root instanceof EspressoRootNode && frame != null) { EspressoRootNode rootNode = (EspressoRootNode) root; - if (rootNode.isBytecodeNode()) { - return rootNode.readBCI(frame); - } + return rootNode.readBCI(frame); } return -1; } @@ -681,17 +680,27 @@ public Class> getLanguageClass() { public Node getInstrumentableNode(Node node) { Node currentNode = node; + // node might be wrapped by instrumentation, so we need to unwrap here while (currentNode != null) { + Node instrumentableNode = null; if (currentNode instanceof EspressoRootNode) { - return ((EspressoRootNode) currentNode).getMethodNode(); + instrumentableNode = ((EspressoRootNode) currentNode).getMethodNode(); } else if (currentNode instanceof EspressoInstrumentableNode) { - return currentNode; - } else if (currentNode instanceof QuickNode) { - QuickNode quickNode = (QuickNode) currentNode; - return quickNode.getBytecodesNode(); + instrumentableNode = currentNode; + } else if (currentNode instanceof BaseQuickNode) { + BaseQuickNode quickNode = (BaseQuickNode) currentNode; + instrumentableNode = quickNode.getBytecodesNode(); } else { currentNode = currentNode.getParent(); } + if (instrumentableNode != null) { + // we found the node, check if wrapped + if (instrumentableNode instanceof WrapperNode) { + return ((WrapperNode) instrumentableNode).getDelegateNode(); + } else { + return instrumentableNode; + } + } } return null; } From 501be3f1a7ac756d5ec323e691d8ed98595d60f6 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 4 May 2021 12:03:55 +0200 Subject: [PATCH 068/290] Cancel the future if the task was not started. Since in "no-background-compilation" mode we wait for the future which could never happen if te task is canceled and removed from the queue, we should cancel the future when the task is canceled. --- .../compiler/truffle/runtime/CompilationTask.java | 14 ++++++++++++-- .../truffle/runtime/TraversingBlockingQueue.java | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java index fa288f4e897f..01fe93ae61a8 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java @@ -26,6 +26,7 @@ import java.lang.ref.WeakReference; import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; @@ -88,17 +89,26 @@ static CompilationTask createCompilationTask(BackgroundCompileQueue.Priority pri } public void awaitCompletion(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - future.get(timeout, unit); + try { + future.get(timeout, unit); + } catch (CancellationException e) { + // Ignored + } } public void awaitCompletion() throws ExecutionException, InterruptedException { - future.get(); + try { + future.get(); + } catch (CancellationException e) { + // Ignored + } } public synchronized boolean cancel() { if (!cancelled) { cancelled = true; if (!started) { + future.cancel(false); finished(); } return true; diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java index cb5aad42ff4e..5120a99976a7 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java @@ -87,6 +87,7 @@ private synchronized Runnable takeMax() { CompilationTask task = task(entry); // updateWeight returns false only if the task's target does not exist if (task.isCancelled() || !task.updateWeight(time)) { + it.remove(); continue; } if (max == null || task.isHigherPriorityThan(task(max))) { From 5379c7053523f7b49b38fb7cf82b70118f482802 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 4 May 2021 14:37:22 +0200 Subject: [PATCH 069/290] Use fixed point math when tracing. --- .../graalvm/compiler/truffle/runtime/FixedPointMath.java | 6 +++--- .../truffle/runtime/debug/TraceCompilationListener.java | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/FixedPointMath.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/FixedPointMath.java index 6951bc3d03b2..382f7e7a7592 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/FixedPointMath.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/FixedPointMath.java @@ -24,7 +24,7 @@ */ package org.graalvm.compiler.truffle.runtime; -final class FixedPointMath { +public final class FixedPointMath { private static final int SCALE = 4; /** @@ -34,11 +34,11 @@ private FixedPointMath() { throw new IllegalStateException("Should not instantiate" + this.getClass().getName()); } - static int toFixedPoint(double x) { + public static int toFixedPoint(double x) { return (int) (x * (1 << SCALE)); } - static int multiply(int fixedPointValue, int anInteger) { + public static int multiply(int fixedPointValue, int anInteger) { return (fixedPointValue * anInteger) >> SCALE; } } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java index 514617590221..7086958cf259 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.truffle.common.TruffleCompilerListener.CompilationResultInfo; import org.graalvm.compiler.truffle.common.TruffleCompilerListener.GraphInfo; import org.graalvm.compiler.truffle.runtime.AbstractGraalTruffleRuntimeListener; +import org.graalvm.compiler.truffle.runtime.FixedPointMath; import org.graalvm.compiler.truffle.runtime.GraalTruffleRuntime; import org.graalvm.compiler.truffle.runtime.GraalTruffleRuntimeListener; import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget; @@ -86,10 +87,10 @@ private Map queueProperties(OptimizedCallTarget target, int tier GraalTruffleRuntimeListener.addASTSizeProperty(target, properties); properties.put("Tier", Integer.toString(tier)); // to avoid padding int callThreshold = tier == 1 ? target.engine.callThresholdInInterpreter : target.engine.callThresholdInFirstTier; - double scale = runtime.compilationThresholdScale(); - properties.put("Calls/Thres", String.format("%7d/%5d", target.getCallCount(), (int) (scale * callThreshold))); + int scale = runtime.compilationThresholdScale(); + properties.put("Calls/Thres", String.format("%7d/%5d", target.getCallCount(), FixedPointMath.multiply(scale, callThreshold))); int callAndLoopThreshold = tier == 1 ? target.engine.callAndLoopThresholdInInterpreter : target.engine.callAndLoopThresholdInFirstTier; - properties.put("CallsAndLoop/Thres", String.format("%7d/%5d", target.getCallAndLoopCount(), (int) (scale * callAndLoopThreshold))); + properties.put("CallsAndLoop/Thres", String.format("%7d/%5d", target.getCallAndLoopCount(), FixedPointMath.multiply(scale, callAndLoopThreshold))); properties.put("Src", formatSourceSection(target.getRootNode().getSourceSection())); properties.put("QueueSize", runtime.getCompilationQueueSize()); properties.put("Time", System.nanoTime() - startTime); From 5201cdf942ae80941497689388b9d46f85759581 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Tue, 4 May 2021 15:13:33 +0200 Subject: [PATCH 070/290] Simplify CLI options that control the dynamic threshold scaling. --- .../options/PolyglotCompilerOptions.java | 12 ++++---- .../runtime/BackgroundCompileQueue.java | 6 ++-- .../runtime/DynamicThresholdsQueue.java | 28 +++++++++++++------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index fef663b7e52e..6aea6f4e47a7 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -516,17 +516,17 @@ private String indent(int nameLength) { @Option(help = "Traversing queue gives first tier compilations priority.", category = OptionCategory.INTERNAL) public static final OptionKey TraversingQueueFirstTierPriority = new OptionKey<>(true); - @Option(help = "TODO", category = OptionCategory.INTERNAL) + @Option(help = "Reduce or increase the compilation threshold depending on the size of the compilation queue.", category = OptionCategory.INTERNAL) public static final OptionKey DynamicCompilationThresholds = new OptionKey<>(true); - @Option(help = "TODO", category = OptionCategory.INTERNAL) + @Option(help = "The minimal scale the compilation thresholds can be reduced to.", category = OptionCategory.INTERNAL) public static final OptionKey DynamicCompilerThresholdsMinScale = new OptionKey<>(0.1); - @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicCompilerThresholdsNormalLoad = new OptionKey<>(100); + @Option(help = "The minimal compilation queue load at which compilation thresholds are not reduced.", category = OptionCategory.INTERNAL) + public static final OptionKey DynamicCompilerThresholdsMinNormalLoad = new OptionKey<>(10); - @Option(help = "TODO", category = OptionCategory.INTERNAL) - public static final OptionKey DynamicCompilationThresholdsPlateauWidth = new OptionKey<>(0.9); + @Option(help = "The maximal compilation queue load at which compilation thresholds are not increased.", category = OptionCategory.INTERNAL) + public static final OptionKey DynamicCompilerThresholdsMaxNormalLoad = new OptionKey<>(90); // Language agnostic inlining diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java index bff32b59868c..52f27b68834e 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java @@ -155,9 +155,9 @@ private BlockingQueue createQueue(OptimizedCallTarget callTarget, int if (callTarget.getOptionValue(PolyglotCompilerOptions.TraversingCompilationQueue)) { if (callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilationThresholds)) { double minScale = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsMinScale); - int normalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsNormalLoad); - double plateauWidth = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilationThresholdsPlateauWidth); - return new DynamicThresholdsQueue(runtime, threads, minScale, normalLoad, plateauWidth); + int minNormalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsMinNormalLoad); + int maxNormalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsMaxNormalLoad); + return new DynamicThresholdsQueue(runtime, threads, minScale, minNormalLoad, maxNormalLoad); } else { return new TraversingBlockingQueue(); } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 19300e0ee637..15581ad2cbec 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -31,15 +31,15 @@ public class DynamicThresholdsQueue extends TraversingBlockingQueue { private final GraalTruffleRuntime runtime; private final int threads; private final double minScale; - private final int normalLoad; - private final double plateauWidth; + private final int minNormalLoad; + private final int maxNormalLoad; - public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads, double minScale, int normalLoad, double plateauWidth) { + public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads, double minScale, int minNormalLoad, int maxNormalLoad) { this.runtime = runtime; this.threads = threads; this.minScale = minScale; - this.normalLoad = normalLoad; - this.plateauWidth = plateauWidth; + this.minNormalLoad = minNormalLoad; + this.maxNormalLoad = maxNormalLoad; } private double load() { @@ -80,15 +80,25 @@ private void scaleThresholds() { runtime.setCompilationThresholdScale(FixedPointMath.toFixedPoint(scale())); } + /** + * @return f(x) where x is the load of the queue and f is a function that + * + * - Grows linearly between coordinates (0, minScale) and (minNormalLoad, 1) + * + * - Equals 1 for all x between minNormalLoad and maxNormalLoad (inclusively) + * + * - For all x > maxNormalLoad - grows at the same rate as for x < minNormalLoad, but + * starting at coordinate (maxNormalLoad, 1) + */ private double scale() { double x = load(); - if ((1 - plateauWidth) * normalLoad <= x && x <= (1 + plateauWidth) * normalLoad) { + if (minNormalLoad <= x && x <= maxNormalLoad) { return 1; } - double slope = (1 - minScale) / ((1 - plateauWidth) * normalLoad); - if (x < (1 - plateauWidth) * normalLoad) { + double slope = (1 - minScale) / (minNormalLoad - 1); + if (x < minNormalLoad) { return slope * x + minScale; } - return slope * x - (1 + plateauWidth) * slope * normalLoad + 1; + return slope * x + (1 - slope * maxNormalLoad); } } From fa6c53e9f7c17f8f70906a6986da54ea0fc6c062 Mon Sep 17 00:00:00 2001 From: Soufiane Nassih Date: Wed, 5 May 2021 03:31:07 +0000 Subject: [PATCH 071/290] Update visualvm to build number 998 --- tools/mx.tools/suite.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/mx.tools/suite.py b/tools/mx.tools/suite.py index 3bea74ae1493..149f5b69fafb 100644 --- a/tools/mx.tools/suite.py +++ b/tools/mx.tools/suite.py @@ -315,31 +315,31 @@ "sourceSha1" : "298db2b3c573f9e76a5a7a60a49c7ceb5ddd35f7", }, "VISUALVM_COMMON" : { - "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-944.tar.gz"], - "sha1" : "bcad60202ad88475c24648b0aed61732c6e769cd", + "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-998.tar.gz"], + "sha1" : "47298748d3fc71da5a1806a31faaeca97f8acc42", }, "VISUALVM_PLATFORM_SPECIFIC" : { "os_arch" : { "linux" : { "amd64" : { - "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-944-linux-amd64.tar.gz"], - "sha1" : "3f276219657688958f919110b12f329a080326c1", + "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-998-linux-amd64.tar.gz"], + "sha1" : "914ade0ad80e52b381efcd8a9622328f33a922ae", }, "aarch64" : { - "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-944-linux-aarch64.tar.gz"], - "sha1" : "bb15cff42c33966f034544599c7dd8458ed0b583", + "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-998-linux-aarch64.tar.gz"], + "sha1" : "77211a4a3a6522551b4c7050165d89020541131c", } }, "darwin" : { "amd64" : { - "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-944-macosx-x86_64.tar.gz"], - "sha1" : "4caf27fd64a4687b1d3d7513a18d55c920122932", + "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-998-macosx-x86_64.tar.gz"], + "sha1" : "26506f4e2376acecc7d5084e4ffc23024ea6dd01", } }, "windows" : { "amd64" : { - "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-944-windows-amd64.tar.gz"], - "sha1" : "81c6e3cea89d0786a45ec940b2cbd05c3624e6e6", + "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/visualvm/visualvm-998-windows-amd64.tar.gz"], + "sha1" : "71402cf4b4f2ab0f40f52be4e057e464423ffe5f", } }, } From c660ca88e9bf0d1036da484bef5fba3ef1cddb0e Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 5 May 2021 13:26:21 +0200 Subject: [PATCH 072/290] extract abstract super class from EspressoStatementNode to simplify hierachy --- .../nodes/BaseEspressoStatementNode.java | 53 +++++++++++++++++ .../truffle/espresso/nodes/BciProvider.java | 30 ++++++++++ .../truffle/espresso/nodes/BytecodeNode.java | 28 ++++----- .../nodes/EspressoInstrumentableNode.java | 6 +- .../espresso/nodes/EspressoRootNode.java | 2 +- .../espresso/nodes/EspressoStatementNode.java | 58 +++---------------- .../nodes/IntrinsicSubstitutorNode.java | 2 +- .../espresso/nodes/NativeMethodNode.java | 2 +- .../espresso/nodes/quick/BaseQuickNode.java | 8 ++- .../espresso/nodes/quick/CheckCastNode.java | 2 +- .../espresso/nodes/quick/QuickNode.java | 12 ++-- .../quick/interop/ByteArrayLoadNode.java | 2 +- .../quick/interop/ByteArrayStoreNode.java | 2 +- .../quick/interop/CharArrayLoadNode.java | 2 +- .../quick/interop/CharArrayStoreNode.java | 2 +- .../quick/interop/DoubleArrayLoadNode.java | 2 +- .../quick/interop/DoubleArrayStoreNode.java | 2 +- .../quick/interop/FloatArrayLoadNode.java | 2 +- .../quick/interop/FloatArrayStoreNode.java | 2 +- .../nodes/quick/interop/IntArrayLoadNode.java | 2 +- .../quick/interop/IntArrayStoreNode.java | 2 +- .../quick/interop/LongArrayLoadNode.java | 2 +- .../quick/interop/LongArrayStoreNode.java | 2 +- .../quick/interop/QuickenedGetFieldNode.java | 2 +- .../quick/interop/QuickenedPutFieldNode.java | 2 +- .../quick/interop/ReferenceArrayLoadNode.java | 2 +- .../interop/ReferenceArrayStoreNode.java | 2 +- .../quick/interop/ShortArrayLoadNode.java | 2 +- .../quick/interop/ShortArrayStoreNode.java | 2 +- .../nodes/quick/invoke/InlinedGetterNode.java | 2 +- .../nodes/quick/invoke/InlinedSetterNode.java | 2 +- .../invoke/LeafAssumptionGetterNode.java | 2 +- .../invoke/LeafAssumptionSetterNode.java | 2 +- .../espresso/runtime/JDWPContextImpl.java | 29 ++++++++-- .../truffle/espresso/vm/InterpreterToVM.java | 2 +- 35 files changed, 169 insertions(+), 109 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BciProvider.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java new file mode 100644 index 000000000000..107a203c415a --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, 2021, 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.nodes; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrumentation.GenerateWrapper; +import com.oracle.truffle.api.instrumentation.InstrumentableNode; +import com.oracle.truffle.api.instrumentation.ProbeNode; +import com.oracle.truffle.api.instrumentation.StandardTags; +import com.oracle.truffle.api.instrumentation.Tag; +import com.oracle.truffle.api.nodes.Node; + +@GenerateWrapper +public abstract class BaseEspressoStatementNode extends Node implements InstrumentableNode { + + public void execute(@SuppressWarnings("unused") VirtualFrame frame) { + // only here to satisfy wrapper generation + } + + public boolean hasTag(Class tag) { + return tag == StandardTags.StatementTag.class; + } + + @Override + public boolean isInstrumentable() { + return true; + } + + @Override + public WrapperNode createWrapper(ProbeNode probe) { + return new BaseEspressoStatementNodeWrapper(this, probe); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BciProvider.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BciProvider.java new file mode 100644 index 000000000000..39ecb65eb060 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BciProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, 2021, 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.nodes; + +import com.oracle.truffle.api.frame.Frame; + +public interface BciProvider { + + int getBci(Frame frame); +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 2d874e189463..a805956569ff 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -1267,7 +1267,7 @@ private EspressoRootNode getRoot() { } @Override - public int getCurrentBCI(Frame frame) { + public int getBci(Frame frame) { try { assert bciSlot != null; return frame.getInt(bciSlot); @@ -1297,7 +1297,7 @@ public InstrumentableNode materializeInstrumentableNodes(Set tag) { - return tag == StandardTags.StatementTag.class; - } - - public BytecodeNode getBytecodesNode() { - // parent is normally the BytecodeNode.InstrumentationSupport - // parents parent is normally the BytecodeNode - Node parent = getParent(); - - while (parent instanceof WrapperNode || parent instanceof BytecodeNode.InstrumentationSupport) { - parent = parent.getParent(); - } - assert !(parent instanceof WrapperNode); - return (BytecodeNode) parent; + Source s = parent.getSource(); + // when there is a line number table we also have a source + assert s != null; + return s.createSection(lineNumber); } @Override - public int getCurrentBCI(@SuppressWarnings("unused") Frame frame) { + public int getBci(@SuppressWarnings("unused") Frame frame) { return startBci; } - - @Override - public Method getMethod() { - return getBytecodesNode().getMethod(); - } - - @Override - public EspressoContext getContext() { - return getBytecodesNode().getContext(); - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java index 59c9374ed977..4217dadc04b0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java @@ -96,7 +96,7 @@ public Node copy() { } @Override - public int getCurrentBCI(@SuppressWarnings("unused") Frame frame) { + public int getBci(@SuppressWarnings("unused") Frame frame) { return -2; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java index 77d09958c51d..d8e7cf4a29b0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java @@ -144,7 +144,7 @@ protected Object processResult(JniEnv env, Object result) { } @Override - public int getCurrentBCI(@SuppressWarnings("unused") Frame frame) { + public int getBci(@SuppressWarnings("unused") Frame frame) { return -2; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java index 9ac2d4050366..6c9dabe8f530 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java @@ -27,9 +27,11 @@ import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.instrumentation.ProbeNode; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.espresso.nodes.BciProvider; +import com.oracle.truffle.espresso.nodes.BytecodeNode; @GenerateWrapper -public abstract class BaseQuickNode extends Node implements InstrumentableNode { +public abstract class BaseQuickNode extends Node implements BciProvider, InstrumentableNode { public abstract int execute(VirtualFrame frame, long[] primitives, Object[] refs); @@ -48,5 +50,7 @@ public boolean removedByRedefintion() { return false; } - public abstract Node getBytecodesNode(); + public final BytecodeNode getBytecodeNode() { + return (BytecodeNode) getParent(); + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/CheckCastNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/CheckCastNode.java index a567fee54a99..231a40a9f8a9 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/CheckCastNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/CheckCastNode.java @@ -50,7 +50,7 @@ public boolean producedForeignObject(Object[] refs) { @Override public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - BytecodeNode root = getBytecodesNode(); + BytecodeNode root = getBytecodeNode(); StaticObject receiver = BytecodeNode.peekObject(refs, top - 1); if (StaticObject.isNull(receiver) || typeCheckNode.executeTypeCheck(typeToCheck, receiver.getKlass())) { return 0; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java index 46a1e7cee922..b7a87371413f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/QuickNode.java @@ -24,9 +24,9 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.espresso.nodes.BytecodeNode; import com.oracle.truffle.espresso.runtime.StaticObject; public abstract class QuickNode extends BaseQuickNode { @@ -58,22 +58,18 @@ protected QuickNode(int top, int callerBCI) { protected final StaticObject nullCheck(StaticObject value) { if (StaticObject.isNull(value)) { enterExceptionProfile(); - throw getBytecodesNode().getMeta().throwNullPointerException(); + throw getBytecodeNode().getMeta().throwNullPointerException(); } return value; } @Override - public final BytecodeNode getBytecodesNode() { - return (BytecodeNode) getParent(); - } - - public int getBCI() { + public int getBci(@SuppressWarnings("unused") Frame frame) { return callerBCI; } @Override public SourceSection getSourceSection() { - return getBytecodesNode().getSourceSectionAtBCI(callerBCI); + return getBytecodeNode().getSourceSectionAtBCI(callerBCI); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayLoadNode.java index 9771f15deedb..329caf00957e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayLoadNode.java @@ -87,7 +87,7 @@ byte doForeign(StaticObject array, int index, @Specialization(guards = "array.isEspressoObject()") byte doEspresso(StaticObject array, int index) { - return getBytecodesNode().getInterpreterToVM().getArrayByte(index, array); + return getBytecodeNode().getInterpreterToVM().getArrayByte(index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayStoreNode.java index b9f265a50cc2..d710e2ada889 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayStoreNode.java @@ -71,7 +71,7 @@ void doForeign(StaticObject array, int index, byte value, @Specialization(guards = "array.isEspressoObject()") void doEspresso(StaticObject array, int index, byte value) { - getBytecodesNode().getInterpreterToVM().setArrayByte(value, index, array); + getBytecodeNode().getInterpreterToVM().setArrayByte(value, index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayLoadNode.java index a5b63c045fa2..f54f070a237c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayLoadNode.java @@ -76,7 +76,7 @@ char doForeign(StaticObject array, int index, @Specialization(guards = "array.isEspressoObject()") char doEspresso(StaticObject array, int index) { - return getBytecodesNode().getInterpreterToVM().getArrayChar(index, array); + return getBytecodeNode().getInterpreterToVM().getArrayChar(index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayStoreNode.java index 270a18cdf220..68ca76eda301 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayStoreNode.java @@ -65,7 +65,7 @@ void doForeign(StaticObject array, int index, char value, @Specialization(guards = "array.isEspressoObject()") void doEspresso(StaticObject array, int index, char value) { - getBytecodesNode().getInterpreterToVM().setArrayChar(value, index, array); + getBytecodeNode().getInterpreterToVM().setArrayChar(value, index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayLoadNode.java index d3e0b4e4848d..aac75b10bb29 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayLoadNode.java @@ -76,7 +76,7 @@ public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { @Specialization(guards = "array.isEspressoObject()") double doEspresso(StaticObject array, int index) { - return getBytecodesNode().getInterpreterToVM().getArrayDouble(index, array); + return getBytecodeNode().getInterpreterToVM().getArrayDouble(index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayStoreNode.java index 8835090cbfcd..f7939c3cc33b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayStoreNode.java @@ -65,7 +65,7 @@ void doForeign(StaticObject array, int index, double value, @Specialization(guards = "array.isEspressoObject()") void doEspresso(StaticObject array, int index, double value) { - getBytecodesNode().getInterpreterToVM().setArrayDouble(value, index, array); + getBytecodeNode().getInterpreterToVM().setArrayDouble(value, index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayLoadNode.java index 639270fdc3fb..6ca894eb4d03 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayLoadNode.java @@ -76,7 +76,7 @@ float doForeign(StaticObject array, int index, @Specialization(guards = "array.isEspressoObject()") float doEspresso(StaticObject array, int index) { - return getBytecodesNode().getInterpreterToVM().getArrayFloat(index, array); + return getBytecodeNode().getInterpreterToVM().getArrayFloat(index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayStoreNode.java index c2c57f075c93..ed7537de3cd9 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayStoreNode.java @@ -65,7 +65,7 @@ void doForeign(StaticObject array, int index, float value, @Specialization(guards = "array.isEspressoObject()") void doEspresso(StaticObject array, int index, float value) { - getBytecodesNode().getInterpreterToVM().setArrayFloat(value, index, array); + getBytecodeNode().getInterpreterToVM().setArrayFloat(value, index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayLoadNode.java index fb41c4f566f0..a6b58503aa04 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayLoadNode.java @@ -76,7 +76,7 @@ int doForeign(StaticObject array, int index, @Specialization(guards = "array.isEspressoObject()") int doEspresso(StaticObject array, int index) { - return getBytecodesNode().getInterpreterToVM().getArrayInt(index, array); + return getBytecodeNode().getInterpreterToVM().getArrayInt(index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayStoreNode.java index 0d7b38b8eccb..4d5c92446042 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayStoreNode.java @@ -65,7 +65,7 @@ void doForeign(StaticObject array, int index, int value, @Specialization(guards = "array.isEspressoObject()") void doEspresso(StaticObject array, int index, int value) { - getBytecodesNode().getInterpreterToVM().setArrayInt(value, index, array); + getBytecodeNode().getInterpreterToVM().setArrayInt(value, index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayLoadNode.java index 9e8524d9fa96..25228264bbc3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayLoadNode.java @@ -76,7 +76,7 @@ long doForeign(StaticObject array, int index, @Specialization(guards = "array.isEspressoObject()") long doEspresso(StaticObject array, int index) { - return getBytecodesNode().getInterpreterToVM().getArrayLong(index, array); + return getBytecodeNode().getInterpreterToVM().getArrayLong(index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayStoreNode.java index 8ef3d58e0d58..63a69a8e60ff 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayStoreNode.java @@ -65,7 +65,7 @@ void doForeign(StaticObject array, int index, long value, @Specialization(guards = "array.isEspressoObject()") void doEspresso(StaticObject array, int index, long value) { - getBytecodesNode().getInterpreterToVM().setArrayLong(value, index, array); + getBytecodeNode().getInterpreterToVM().setArrayLong(value, index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedGetFieldNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedGetFieldNode.java index a996d0dc08fd..8892b3d377c9 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedGetFieldNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedGetFieldNode.java @@ -45,7 +45,7 @@ public QuickenedGetFieldNode(int top, int callerBCI, int statementIndex, Field f @Override public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - BytecodeNode root = getBytecodesNode(); + BytecodeNode root = getBytecodeNode(); StaticObject receiver = nullCheck(BytecodeNode.popObject(refs, top - 1)); return getFieldNode.getField(frame, primitives, refs, root, receiver, top - 1, statementIndex) - 1; // -receiver } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedPutFieldNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedPutFieldNode.java index 6e64f89b19c8..667cb029a325 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedPutFieldNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedPutFieldNode.java @@ -45,7 +45,7 @@ public QuickenedPutFieldNode(int top, int callerBCI, Field field, int statementI @Override public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - BytecodeNode root = getBytecodesNode(); + BytecodeNode root = getBytecodeNode(); StaticObject receiver = nullCheck(BytecodeNode.popObject(refs, top - 1 - slotCount)); setFieldNode.setField(frame, primitives, refs, root, receiver, top, statementIndex); return -slotCount - 1; // -receiver diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java index 9c7b40ef3188..69b9bbc66619 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java @@ -78,7 +78,7 @@ StaticObject doForeign(StaticObject array, int index, @Specialization(guards = "array.isEspressoObject()") StaticObject doEspresso(StaticObject array, int index) { - return getBytecodesNode().getInterpreterToVM().getArrayObject(index, array); + return getBytecodeNode().getInterpreterToVM().getArrayObject(index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayStoreNode.java index 542eebcdad0b..e5dda3947194 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayStoreNode.java @@ -66,7 +66,7 @@ void doForeign(StaticObject array, int index, StaticObject value, @Specialization(guards = "array.isEspressoObject()") void doEspresso(StaticObject array, int index, StaticObject value) { - getBytecodesNode().getInterpreterToVM().setArrayObject(value, index, array); + getBytecodeNode().getInterpreterToVM().setArrayObject(value, index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayLoadNode.java index 68a6395ee8c1..800b54c79116 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayLoadNode.java @@ -76,7 +76,7 @@ short doForeign(StaticObject array, int index, @Specialization(guards = "array.isEspressoObject()") short doEspresso(StaticObject array, int index) { - return getBytecodesNode().getInterpreterToVM().getArrayShort(index, array); + return getBytecodeNode().getInterpreterToVM().getArrayShort(index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayStoreNode.java index 7c583e81bf9e..b11ae66d5a02 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayStoreNode.java @@ -65,7 +65,7 @@ void doForeign(StaticObject array, int index, short value, @Specialization(guards = "array.isEspressoObject()") void doEspresso(StaticObject array, int index, short value) { - getBytecodesNode().getInterpreterToVM().setArrayShort(value, index, array); + getBytecodeNode().getInterpreterToVM().setArrayShort(value, index, array); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedGetterNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedGetterNode.java index 761757550ba7..a2fbff0873e1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedGetterNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedGetterNode.java @@ -67,7 +67,7 @@ public static InlinedGetterNode create(Method inlinedMethod, int top, int opCode @Override public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - BytecodeNode root = getBytecodesNode(); + BytecodeNode root = getBytecodeNode(); StaticObject receiver = field.isStatic() ? field.getDeclaringKlass().tryInitializeAndGetStatics() : nullCheck(BytecodeNode.popObject(refs, top - 1)); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedSetterNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedSetterNode.java index c23be8ad14b4..b872eadf6530 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedSetterNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedSetterNode.java @@ -72,7 +72,7 @@ public static InlinedSetterNode create(Method inlinedMethod, int top, int opCode @Override public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - BytecodeNode root = getBytecodesNode(); + BytecodeNode root = getBytecodeNode(); StaticObject receiver = field.isStatic() ? field.getDeclaringKlass().tryInitializeAndGetStatics() : nullCheck(BytecodeNode.popObject(refs, top - 1 - slotCount)); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/LeafAssumptionGetterNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/LeafAssumptionGetterNode.java index 64d96e461e05..96ada5514c98 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/LeafAssumptionGetterNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/LeafAssumptionGetterNode.java @@ -40,7 +40,7 @@ protected LeafAssumptionGetterNode(Method inlinedMethod, int top, int opCode, in @Override public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - BytecodeNode root = getBytecodesNode(); + BytecodeNode root = getBytecodeNode(); if (inlinedMethod.leafAssumption()) { StaticObject receiver = field.isStatic() ? field.getDeclaringKlass().tryInitializeAndGetStatics() diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/LeafAssumptionSetterNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/LeafAssumptionSetterNode.java index 61edaf3bb7df..02c2fd47a822 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/LeafAssumptionSetterNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/LeafAssumptionSetterNode.java @@ -40,7 +40,7 @@ protected LeafAssumptionSetterNode(Method inlinedMethod, int top, int opCode, in @Override public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - BytecodeNode root = getBytecodesNode(); + BytecodeNode root = getBytecodeNode(); if (inlinedMethod.leafAssumption()) { StaticObject receiver = field.isStatic() ? field.getDeclaringKlass().tryInitializeAndGetStatics() 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 340a624282a5..f7cef7371cd2 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 @@ -69,6 +69,7 @@ import com.oracle.truffle.espresso.jdwp.impl.JDWPLogger; import com.oracle.truffle.espresso.jdwp.impl.TypeTag; import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.nodes.BciProvider; import com.oracle.truffle.espresso.nodes.EspressoInstrumentableNode; import com.oracle.truffle.espresso.nodes.EspressoRootNode; import com.oracle.truffle.espresso.nodes.quick.BaseQuickNode; @@ -678,6 +679,11 @@ public Class> getLanguageClass() { @Override public Node getInstrumentableNode(Node node) { + if (node instanceof EspressoInstrumentableNode) { + return node; + } else if (node instanceof BaseQuickNode) { + return ((BaseQuickNode) node).getBytecodeNode(); + } Node currentNode = node; // node might be wrapped by instrumentation, so we need to unwrap here @@ -689,7 +695,7 @@ public Node getInstrumentableNode(Node node) { instrumentableNode = currentNode; } else if (currentNode instanceof BaseQuickNode) { BaseQuickNode quickNode = (BaseQuickNode) currentNode; - instrumentableNode = quickNode.getBytecodesNode(); + instrumentableNode = quickNode.getBytecodeNode(); } else { currentNode = currentNode.getParent(); } @@ -705,14 +711,27 @@ public Node getInstrumentableNode(Node node) { return null; } + private static BciProvider getBciProviderNode(Node node) { + if (node instanceof BciProvider) { + return (BciProvider) node; + } + Node currentNode = node.getParent(); + while (currentNode != null) { + if (currentNode instanceof BciProvider) { + return (BciProvider) currentNode; + } + currentNode = currentNode.getParent(); + } + return null; + } + @Override public long getBCI(Node rawNode, Frame frame) { - Node node = getInstrumentableNode(rawNode); - if (node == null) { + BciProvider bciProvider = getBciProviderNode(rawNode); + if (bciProvider == null) { return -1; } - EspressoInstrumentableNode instrumentableNode = (EspressoInstrumentableNode) node; - return instrumentableNode.getCurrentBCI(frame); + return bciProvider.getBci(frame); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java index 387c29279cfb..5539da126198 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java @@ -634,7 +634,7 @@ public static StaticObject fillInStackTrace(@Host(Throwable.class) StaticObject Node location = element.getLocation(); while (location != null) { if (location instanceof QuickNode) { - bci = ((QuickNode) location).getBCI(); + bci = ((QuickNode) location).getBci(element.getFrame()); break; } location = location.getParent(); From 8d780a11984715a80666377e445e655fe32ad108 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 20 Apr 2021 17:54:36 -0700 Subject: [PATCH 073/290] Disable experimental FIPS mode. --- .../svm/core/jdk/SecuritySubstitutions.java | 16 ++++++++++++++++ .../svm/hosted/SecurityServicesFeature.java | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java index 2f0063be528f..1141c83476cd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java @@ -571,6 +571,22 @@ final class Target_sun_security_jca_ProviderConfig_ProviderLoader { static Target_sun_security_jca_ProviderConfig_ProviderLoader INSTANCE; } +/** + * This only applies to JDK8 and JDK11. Experimental FIPS mode in the SunJSSE Provider was removed + * in JDK-8217835. Going forward it is recommended to configure FIPS 140 compliant cryptography + * providers by using the usual JCA providers configuration mechanism. + */ +@SuppressWarnings("unused") +@TargetClass(value = sun.security.ssl.SunJSSE.class, onlyWith = JDK11OrEarlier.class) +final class Target_sun_security_ssl_SunJSSE { + + @Substitute + private Target_sun_security_ssl_SunJSSE(java.security.Provider cryptoProvider, String providerName) { + throw VMError.unsupportedFeature("Experimental FIPS mode in the SunJSSE Provider is deprecated (JDK-8217835)." + + " To register a FIPS provider use the supported java.security.Security.addProvider() API."); + } +} + /** Dummy class to have a class with the file's name. */ public final class SecuritySubstitutions { } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java index b1f93079272f..095530853d51 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java @@ -153,6 +153,23 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { public void afterRegistration(AfterRegistrationAccess a) { ModuleSupport.exportAndOpenPackageToClass("java.base", "sun.security.x509", false, getClass()); ModuleSupport.openModuleByClass(Security.class, getClass()); + disableExperimentalFipsMode(a); + } + + /** + * The SunJSSE provider had a experimental feature that bound to a FIPS crypto provider. This + * has been removed in JDK-8217835. We disabled explicitly here by calling SunJSSE.isFIPS(). If + * it was already enabled that's an error. + */ + private static void disableExperimentalFipsMode(AfterRegistrationAccess a) { + if (JavaVersionUtil.JAVA_SPEC <= 11) { + try { + Boolean isFIPS = (Boolean) method(a, "sun.security.ssl.SunJSSE", "isFIPS").invoke(null); + VMError.guarantee(!isFIPS, "SunJSSE is already initialized in experimental FIPS mode."); + } catch (IllegalAccessException | InvocationTargetException e) { + VMError.shouldNotReachHere(e); + } + } } @Override From 4142e16bb66e139c17f67f2d7cbe1586c64eb38e Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Wed, 5 May 2021 17:30:54 +0200 Subject: [PATCH 074/290] Update changelog and general cleanup. --- compiler/mx.compiler/mx_compiler.py | 1 + .../truffle/options/PolyglotCompilerOptions.java | 4 ++-- .../truffle/runtime/BackgroundCompileQueue.java | 2 +- .../compiler/truffle/runtime/CompilationTask.java | 8 ++++---- .../truffle/runtime/DynamicThresholdsQueue.java | 11 +++++------ .../truffle/runtime/TraversingBlockingQueue.java | 2 +- truffle/CHANGELOG.md | 1 + 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/compiler/mx.compiler/mx_compiler.py b/compiler/mx.compiler/mx_compiler.py index 6948b82e228c..b55e715b6102 100644 --- a/compiler/mx.compiler/mx_compiler.py +++ b/compiler/mx.compiler/mx_compiler.py @@ -694,6 +694,7 @@ def _unittest_config_participant(config): if _get_XX_option_value(vmArgs, 'TypeProfileWidth', None) is None: vmArgs.append('-XX:TypeProfileWidth=8') + # TODO: GR-31197, this should be removed. vmArgs.append('-Dpolyglot.engine.DynamicCompilationThresholds=false') vmArgs.append('-Dpolyglot.engine.AllowExperimentalOptions=true') return (vmArgs, mainClass, mainClassArgs) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index 6aea6f4e47a7..52272648b1b8 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -522,10 +522,10 @@ private String indent(int nameLength) { @Option(help = "The minimal scale the compilation thresholds can be reduced to.", category = OptionCategory.INTERNAL) public static final OptionKey DynamicCompilerThresholdsMinScale = new OptionKey<>(0.1); - @Option(help = "The minimal compilation queue load at which compilation thresholds are not reduced.", category = OptionCategory.INTERNAL) + @Option(help = "The minimal compilation queue load at which compilation thresholds are not reduced. The load is scaled by the number of compiler threads.", category = OptionCategory.INTERNAL) public static final OptionKey DynamicCompilerThresholdsMinNormalLoad = new OptionKey<>(10); - @Option(help = "The maximal compilation queue load at which compilation thresholds are not increased.", category = OptionCategory.INTERNAL) + @Option(help = "The maximal compilation queue load at which compilation thresholds are not increased. The load is scaled by the number of compiler threads.", category = OptionCategory.INTERNAL) public static final OptionKey DynamicCompilerThresholdsMaxNormalLoad = new OptionKey<>(90); // Language agnostic inlining diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java index 52f27b68834e..87e15a50ef7e 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java @@ -157,7 +157,7 @@ private BlockingQueue createQueue(OptimizedCallTarget callTarget, int double minScale = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsMinScale); int minNormalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsMinNormalLoad); int maxNormalLoad = callTarget.getOptionValue(PolyglotCompilerOptions.DynamicCompilerThresholdsMaxNormalLoad); - return new DynamicThresholdsQueue(runtime, threads, minScale, minNormalLoad, maxNormalLoad); + return new DynamicThresholdsQueue(threads, minScale, minNormalLoad, maxNormalLoad); } else { return new TraversingBlockingQueue(); } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java index 01fe93ae61a8..5c462ce0ad15 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/CompilationTask.java @@ -40,7 +40,7 @@ public final class CompilationTask implements TruffleCompilationTask, Callable, Comparable { - private static final Consumer compilationAction = new Consumer() { + private static final Consumer COMPILATION_ACTION = new Consumer() { @Override public void accept(CompilationTask task) { OptimizedCallTarget callTarget = task.targetRef.get(); @@ -85,7 +85,7 @@ static CompilationTask createInitializationTask(WeakReference targetRef, long id) { - return new CompilationTask(priority, targetRef, compilationAction, id); + return new CompilationTask(priority, targetRef, COMPILATION_ACTION, id); } public void awaitCompletion(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { @@ -207,7 +207,7 @@ public Void call() throws Exception { * corrupt a queue data structure. */ boolean isHigherPriorityThan(CompilationTask other) { - if (action != compilationAction) { + if (action != COMPILATION_ACTION) { // Any non-compilation action (e.g. compiler init) is higher priority. return true; } @@ -228,7 +228,7 @@ boolean isHigherPriorityThan(CompilationTask other) { } /** - * @return false if the target reference is null (i.e. if the target was garbage-collected. + * @return false if the target reference is null (i.e. if the target was garbage-collected). */ boolean updateWeight(long currentTime) { OptimizedCallTarget target = targetRef.get(); diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 15581ad2cbec..22df608f04ac 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -26,20 +26,20 @@ import java.util.concurrent.TimeUnit; -public class DynamicThresholdsQueue extends TraversingBlockingQueue { +final class DynamicThresholdsQueue extends TraversingBlockingQueue { - private final GraalTruffleRuntime runtime; private final int threads; private final double minScale; private final int minNormalLoad; private final int maxNormalLoad; + private final double slope; - public DynamicThresholdsQueue(GraalTruffleRuntime runtime, int threads, double minScale, int minNormalLoad, int maxNormalLoad) { - this.runtime = runtime; + DynamicThresholdsQueue(int threads, double minScale, int minNormalLoad, int maxNormalLoad) { this.threads = threads; this.minScale = minScale; this.minNormalLoad = minNormalLoad; this.maxNormalLoad = maxNormalLoad; + this.slope = (1 - minScale) / (minNormalLoad - 1); } private double load() { @@ -77,7 +77,7 @@ public Runnable poll() { } private void scaleThresholds() { - runtime.setCompilationThresholdScale(FixedPointMath.toFixedPoint(scale())); + GraalTruffleRuntime.getRuntime().setCompilationThresholdScale(FixedPointMath.toFixedPoint(scale())); } /** @@ -95,7 +95,6 @@ private double scale() { if (minNormalLoad <= x && x <= maxNormalLoad) { return 1; } - double slope = (1 - minScale) / (minNormalLoad - 1); if (x < minNormalLoad) { return slope * x + minScale; } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java index 5120a99976a7..6d29f5870772 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TraversingBlockingQueue.java @@ -30,7 +30,7 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -public class TraversingBlockingQueue implements BlockingQueue { +class TraversingBlockingQueue implements BlockingQueue { final BlockingQueue entries = new LinkedBlockingDeque<>(); @SuppressWarnings("unchecked") diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index a5caf45eb4b6..6c1345f94782 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -9,6 +9,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan * Added `RootNode#countsTowardsStackTraceLimit()`, replacing `RootNode#isInternal()` as the criterion that determines whether a frame with the given root node counts towards the stack trace limit. * Added `engine.UsePreInitializedContext` option which can be used to disable usage of pre-initialized context. * Added `MemoryFence`: provides methods for fine-grained control of memory ordering. +* Enabled by default the traversing compilation queue with dynamic thresholds, see `PolyglotCompilerOptions#TraversingCompilationQueue`, `PolyglotCompilerOptions#DynamicCompilationThresholds`, `PolyglotCompilerOptions#DynamicCompilerThresholdsMinScale`, `PolyglotCompilerOptions#DynamicCompilerThresholdsMinNormalLoad` and `PolyglotCompilerOptions#DynamicCompilerThresholdsMaxNormalLoad`. ## Version 21.1.0 * Added methods into `Instrumenter` that create bindings to be attached later on. Added `EventBinding.attach()` method. From 699ad00dc4db90004c20288ec612cd89f539fce4 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 6 May 2021 10:52:53 +0200 Subject: [PATCH 075/290] implement NodeLibrary#getScope for QuickNodes and EspressoStatementNodes --- .../truffle/espresso/jdwp/api/CallFrame.java | 30 ++++++++++---- .../espresso/jdwp/api/JDWPContext.java | 23 +++++++---- .../jdwp/impl/DebuggerController.java | 11 ++++- .../nodes/BaseEspressoStatementNode.java | 21 ++++++++++ .../espresso/nodes/EspressoStatementNode.java | 5 +++ .../espresso/nodes/quick/BaseQuickNode.java | 19 +++++++++ .../espresso/runtime/JDWPContextImpl.java | 41 ++++++------------- 7 files changed, 101 insertions(+), 49 deletions(-) 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 b2de552c9790..b05d20c9a0eb 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 @@ -26,6 +26,7 @@ import com.oracle.truffle.api.debug.DebugStackFrame; import com.oracle.truffle.api.debug.DebugValue; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.NodeLibrary; @@ -47,13 +48,14 @@ public final class CallFrame { private final long codeIndex; private final long threadId; private final Frame frame; + private final Node currentNode; private final RootNode rootNode; private final DebugStackFrame debugStackFrame; private final DebugScope debugScope; private final JDWPContext context; private Object scope; - public CallFrame(long threadId, byte typeTag, long classId, MethodRef method, long methodId, long codeIndex, Frame frame, RootNode rootNode, + public CallFrame(long threadId, byte typeTag, long classId, MethodRef method, long methodId, long codeIndex, Frame frame, Node currentNode, RootNode rootNode, DebugStackFrame debugStackFrame, JDWPContext context) { this.threadId = threadId; this.typeTag = typeTag; @@ -62,6 +64,7 @@ public CallFrame(long threadId, byte typeTag, long classId, MethodRef method, lo this.methodId = methodId; this.codeIndex = method != null && method.isObsolete() ? -1 : codeIndex; this.frame = frame; + this.currentNode = currentNode; this.rootNode = rootNode; this.debugStackFrame = debugStackFrame; this.debugScope = debugStackFrame != null ? debugStackFrame.getScope() : null; @@ -69,7 +72,7 @@ public CallFrame(long threadId, byte typeTag, long classId, MethodRef method, lo } public CallFrame(long threadId, byte typeTag, long classId, long methodId, long codeIndex) { - this(threadId, typeTag, classId, null, methodId, codeIndex, null, null, null, null); + this(threadId, typeTag, classId, null, methodId, codeIndex, null, null, null, null, null); } public byte getTypeTag() { @@ -142,15 +145,24 @@ private Object getScope() { if (scope != null) { return scope; } - Node instrumentableNode = context.getInstrumentableNode(rootNode); - - if (instrumentableNode == null) { - JDWPLogger.log("Unable to get instrumentable node for root %s", JDWPLogger.LogLevel.ALL, rootNode); - } else { + // check if current node has scope + if (NodeLibrary.getUncached().hasScope(currentNode, frame)) { try { - scope = NodeLibrary.getUncached().getScope(instrumentableNode, frame, true); + scope = NodeLibrary.getUncached().getScope(currentNode, frame, true); } catch (UnsupportedMessageException e) { - JDWPLogger.log("Unable to get scope for %s", JDWPLogger.LogLevel.ALL, instrumentableNode.getClass()); + JDWPLogger.log("Unable to get scope for %s", JDWPLogger.LogLevel.ALL, currentNode.getClass()); + } + } else { + // fallback to lookup scope provider node from the root node + InstrumentableNode scopeNode = context.getScopeProviderNode(rootNode, frame); + if (scopeNode == null) { + JDWPLogger.log("Unable to get instrumentable node for root %s", JDWPLogger.LogLevel.ALL, rootNode); + } else { + try { + scope = NodeLibrary.getUncached().getScope(scopeNode, frame, true); + } catch (UnsupportedMessageException e) { + JDWPLogger.log("Unable to get scope for %s", JDWPLogger.LogLevel.ALL, scopeNode.getClass()); + } } } return scope; 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 dc0d84cac66c..ebba0d16af18 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 @@ -27,6 +27,7 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; @@ -482,15 +483,6 @@ public interface JDWPContext { */ void abort(int exitCode); - /** - * Returns the nearest {@link com.oracle.truffle.api.instrumentation.InstrumentableNode node} or - * null. The nodes are traversed by walking the parent node hierarchy. - * - * @param node the node - * @return the nearest instrumentable node - */ - Node getInstrumentableNode(Node node); - /** * Returns the current BCI of the node. * @@ -499,4 +491,17 @@ public interface JDWPContext { * @return the current bci */ long getBCI(Node rawNode, Frame frame); + + /** + * Returns a node that returns true for + * {@link com.oracle.truffle.api.interop.NodeLibrary#hasScope(Object, Frame)} or + * null if not present for this root node. The returned node must implement + * {@link InstrumentableNode} and return true for {@link InstrumentableNode#isInstrumentable()} + * + * @param rootNode the root node + * @param frame + * @return the nearest instrumentable node or null + */ + InstrumentableNode getScopeProviderNode(RootNode rootNode, Frame frame); + } 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 b10633aa973e..68af16f5e1e0 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 @@ -713,7 +713,14 @@ public Object visitFrame(FrameInstance frameInstance) { if (codeIndex > lastLineBCI) { codeIndex = lastLineBCI; } - callFrames.add(new CallFrame(context.getIds().getIdAsLong(guestThread), typeTag, klassId, method, methodId, codeIndex, frame, root, null, context)); + Node currentNode = frameInstance.getCallNode(); + if (currentNode == null) { + CallTarget callTarget = frameInstance.getCallTarget(); + if (callTarget instanceof RootCallTarget) { + currentNode = ((RootCallTarget) callTarget).getRootNode(); + } + } + callFrames.add(new CallFrame(context.getIds().getIdAsLong(guestThread), typeTag, klassId, method, methodId, codeIndex, frame, currentNode, root, null, context)); return null; } }); @@ -1010,7 +1017,7 @@ private CallFrame[] createCallFrames(long threadId, Iterable st codeIndex = context.getBCI(rawNode, rawFrame); } - list.addLast(new CallFrame(threadId, typeTag, klassId, method, methodId, codeIndex, rawFrame, root, frame, context)); + list.addLast(new CallFrame(threadId, typeTag, klassId, method, methodId, codeIndex, rawFrame, rawNode, root, frame, context)); frameCount++; if (frameLimit != -1 && frameCount >= frameLimit) { return list.toArray(new CallFrame[list.size()]); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java index 107a203c415a..f5dcf69b4c59 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java @@ -22,15 +22,21 @@ */ package com.oracle.truffle.espresso.nodes; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.instrumentation.ProbeNode; import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.instrumentation.Tag; +import com.oracle.truffle.api.interop.NodeLibrary; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.Node; @GenerateWrapper +@ExportLibrary(NodeLibrary.class) public abstract class BaseEspressoStatementNode extends Node implements InstrumentableNode { public void execute(@SuppressWarnings("unused") VirtualFrame frame) { @@ -50,4 +56,19 @@ public boolean isInstrumentable() { public WrapperNode createWrapper(ProbeNode probe) { return new BaseEspressoStatementNodeWrapper(this, probe); } + + public abstract BytecodeNode getBytecodeNode(); + + @ExportMessage + @SuppressWarnings("static-method") + public final boolean hasScope(@SuppressWarnings("unused") Frame frame) { + return true; + } + + @ExportMessage + @CompilerDirectives.TruffleBoundary + @SuppressWarnings("static-method") + public final Object getScope(Frame frame, @SuppressWarnings("unused") boolean nodeEnter) { + return getBytecodeNode().getScope(frame, nodeEnter); + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoStatementNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoStatementNode.java index a6b3406464a5..a08274106f57 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoStatementNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoStatementNode.java @@ -53,4 +53,9 @@ public SourceSection getSourceSection() { public int getBci(@SuppressWarnings("unused") Frame frame) { return startBci; } + + @Override + public BytecodeNode getBytecodeNode() { + return parent; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java index 6c9dabe8f530..655614dec120 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java @@ -22,15 +22,21 @@ */ package com.oracle.truffle.espresso.nodes.quick; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.instrumentation.ProbeNode; +import com.oracle.truffle.api.interop.NodeLibrary; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.espresso.nodes.BciProvider; import com.oracle.truffle.espresso.nodes.BytecodeNode; @GenerateWrapper +@ExportLibrary(NodeLibrary.class) public abstract class BaseQuickNode extends Node implements BciProvider, InstrumentableNode { public abstract int execute(VirtualFrame frame, long[] primitives, Object[] refs); @@ -53,4 +59,17 @@ public boolean removedByRedefintion() { public final BytecodeNode getBytecodeNode() { return (BytecodeNode) getParent(); } + + @ExportMessage + @SuppressWarnings("static-method") + public final boolean hasScope(@SuppressWarnings("unused") Frame frame) { + return true; + } + + @ExportMessage + @CompilerDirectives.TruffleBoundary + @SuppressWarnings("static-method") + public final Object getScope(Frame frame, @SuppressWarnings("unused") boolean nodeEnter) { + return getBytecodeNode().getScope(frame, nodeEnter); + } } 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 f7cef7371cd2..0890b8630701 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 @@ -34,8 +34,10 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode; import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.NodeLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; @@ -70,9 +72,7 @@ import com.oracle.truffle.espresso.jdwp.impl.TypeTag; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.nodes.BciProvider; -import com.oracle.truffle.espresso.nodes.EspressoInstrumentableNode; import com.oracle.truffle.espresso.nodes.EspressoRootNode; -import com.oracle.truffle.espresso.nodes.quick.BaseQuickNode; import com.oracle.truffle.espresso.nodes.quick.interop.ForeignArrayUtils; import com.oracle.truffle.espresso.runtime.dispatch.EspressoInterop; import com.oracle.truffle.espresso.substitutions.Target_java_lang_Thread; @@ -610,7 +610,7 @@ public CallFrame locateObjectWaitFrame() { Object currentThread = asGuestThread(Thread.currentThread()); KlassRef klass = context.getMeta().java_lang_Object; MethodRef method = context.getMeta().java_lang_Object_wait.getMethodVersion(); - return new CallFrame(ids.getIdAsLong(currentThread), TypeTag.CLASS, ids.getIdAsLong(klass), method, ids.getIdAsLong(method), 0, null, null, null, null); + return new CallFrame(ids.getIdAsLong(currentThread), TypeTag.CLASS, ids.getIdAsLong(klass), method, ids.getIdAsLong(method), 0, null, null, null, null, null); } @Override @@ -678,33 +678,16 @@ public Class> getLanguageClass() { } @Override - public Node getInstrumentableNode(Node node) { - if (node instanceof EspressoInstrumentableNode) { - return node; - } else if (node instanceof BaseQuickNode) { - return ((BaseQuickNode) node).getBytecodeNode(); - } - Node currentNode = node; - - // node might be wrapped by instrumentation, so we need to unwrap here - while (currentNode != null) { - Node instrumentableNode = null; - if (currentNode instanceof EspressoRootNode) { - instrumentableNode = ((EspressoRootNode) currentNode).getMethodNode(); - } else if (currentNode instanceof EspressoInstrumentableNode) { - instrumentableNode = currentNode; - } else if (currentNode instanceof BaseQuickNode) { - BaseQuickNode quickNode = (BaseQuickNode) currentNode; - instrumentableNode = quickNode.getBytecodeNode(); - } else { - currentNode = currentNode.getParent(); + public InstrumentableNode getScopeProviderNode(RootNode rootNode, Frame frame) { + if (rootNode instanceof EspressoRootNode) { + EspressoRootNode espressoRootNode = (EspressoRootNode) rootNode; + Node methodNode = espressoRootNode.getMethodNode(); + if (methodNode instanceof WrapperNode) { + methodNode = ((WrapperNode) methodNode).getDelegateNode(); } - if (instrumentableNode != null) { - // we found the node, check if wrapped - if (instrumentableNode instanceof WrapperNode) { - return ((WrapperNode) instrumentableNode).getDelegateNode(); - } else { - return instrumentableNode; + if (methodNode instanceof InstrumentableNode && ((InstrumentableNode) methodNode).isInstrumentable()) { + if (NodeLibrary.getUncached().hasScope(methodNode, frame)) { + return (InstrumentableNode) methodNode; } } } From d4a651e5bdfc56481a0d5c8cc1341913ae8baed1 Mon Sep 17 00:00:00 2001 From: ivan-ristovic Date: Thu, 6 May 2021 15:58:40 +0200 Subject: [PATCH 076/290] Initial Module substitution rework --- .../svm/core/jdk/Target_java_lang_Module.java | 51 ++----------------- .../jdk/Target_java_lang_WeakPairMap.java | 32 ++++++++++++ 2 files changed, 37 insertions(+), 46 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java index 85afea183b0a..7d0ec96861a2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java @@ -28,7 +28,8 @@ import java.io.InputStream; import java.util.List; -import com.oracle.svm.core.annotate.Delete; +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; @@ -43,51 +44,9 @@ public InputStream getResourceAsStream(String name) { return arr == null ? null : new ByteArrayInputStream(arr.get(0)); } - /* - * All implementations of these stubs are completely empty no-op. This seems appropriate as - * DynamicHub only references a singleton Module implementation anyhow, effectively neutering - * the module system within JDK11. - */ - - @SuppressWarnings({"unused", "static-method"}) - @Substitute - public boolean isReflectivelyExportedOrOpen(String pn, Target_java_lang_Module other, boolean open) { - return true; - } - - @SuppressWarnings({"unused", "static-method"}) - @Substitute - private void implAddReads(Target_java_lang_Module other, boolean syncVM) { - } - - @SuppressWarnings({"unused", "static-method"}) - @Substitute - private void implAddExportsOrOpens(String pn, - Target_java_lang_Module other, - boolean open, - boolean syncVM) { - } - - @SuppressWarnings({"unused", "static-method"}) - @Substitute - void implAddUses(Class service) { - } - - @SuppressWarnings({"unused", "static-method"}) - @Substitute - public boolean canUse(Class service) { - return true; - } - - @SuppressWarnings({"unused", "static-method"}) - @Substitute - public boolean canRead(Target_java_lang_Module other) { - return true; - } - - @Delete @TargetClass(className = "java.lang.Module", innerClass = "ReflectionData", onlyWith = JDK11OrLater.class) - public static final class ReflectionData { + private static final class ReflectionData { + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, isFinal = true, declClassName = "java.lang.WeakPairMap") + static Target_java_lang_WeakPairMap, Boolean> uses; } - } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java new file mode 100644 index 000000000000..7688c2602e17 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020, 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.svm.core.jdk; + +import com.oracle.svm.core.annotate.TargetClass; + +@TargetClass(className = "java.lang.WeakPairMap") +final class Target_java_lang_WeakPairMap { + +} From b6e4c15c209c8c48761ef2c02300f91220b11866 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 6 May 2021 21:39:18 +0200 Subject: [PATCH 077/290] Improve help text for options. --- .../compiler/truffle/options/PolyglotCompilerOptions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java index 52272648b1b8..0181a8bbbfc6 100644 --- a/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.options/src/org/graalvm/compiler/truffle/options/PolyglotCompilerOptions.java @@ -522,10 +522,10 @@ private String indent(int nameLength) { @Option(help = "The minimal scale the compilation thresholds can be reduced to.", category = OptionCategory.INTERNAL) public static final OptionKey DynamicCompilerThresholdsMinScale = new OptionKey<>(0.1); - @Option(help = "The minimal compilation queue load at which compilation thresholds are not reduced. The load is scaled by the number of compiler threads.", category = OptionCategory.INTERNAL) + @Option(help = "The desired minimum compilation queue load. When the load falls bellow this value, the compilation thresholds are decreased. The load is scaled by the number of compiler threads.", category = OptionCategory.INTERNAL) public static final OptionKey DynamicCompilerThresholdsMinNormalLoad = new OptionKey<>(10); - @Option(help = "The maximal compilation queue load at which compilation thresholds are not increased. The load is scaled by the number of compiler threads.", category = OptionCategory.INTERNAL) + @Option(help = "The desired maximum compilation queue load. When the load rises above this value, the compilation thresholds are increased. The load is scaled by the number of compiler threads.", category = OptionCategory.INTERNAL) public static final OptionKey DynamicCompilerThresholdsMaxNormalLoad = new OptionKey<>(90); // Language agnostic inlining From 914be51c007695bfd694b25c3aef5ba33781cb64 Mon Sep 17 00:00:00 2001 From: Boris Spasojevic Date: Thu, 6 May 2021 21:49:59 +0200 Subject: [PATCH 078/290] Correct formula error. --- .../compiler/truffle/runtime/DynamicThresholdsQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java index 22df608f04ac..76d83b7ba8fc 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/DynamicThresholdsQueue.java @@ -39,7 +39,7 @@ final class DynamicThresholdsQueue extends TraversingBlockingQueue { this.minScale = minScale; this.minNormalLoad = minNormalLoad; this.maxNormalLoad = maxNormalLoad; - this.slope = (1 - minScale) / (minNormalLoad - 1); + this.slope = (1 - minScale) / minNormalLoad; } private double load() { From a5c9e2a0ba05f7b56b688b06eabc7a784d3a6330 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Fri, 7 May 2021 12:22:16 +0200 Subject: [PATCH 079/290] fix merge --- .../espresso/runtime/JDWPContextImpl.java | 28 ++----------------- 1 file changed, 3 insertions(+), 25 deletions(-) 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 af337ed9d717..698bebdf80d7 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 @@ -762,30 +762,8 @@ public synchronized int redefineClasses(List redefineInfos) { try { JDWP.LOGGER.fine(() -> "Redefining " + redefineInfos.size() + " classes"); - // list of sub classes that needs to refresh things like vtable - List refreshSubClasses = new ArrayList<>(); - - // match anon inner classes with previous state - List removedInnerClasses = new ArrayList<>(0); - HotSwapClassInfo[] matchedInfos = innerClassRedefiner.matchAnonymousInnerClasses(redefineInfos, removedInnerClasses); - - // detect all changes to all classes, throws if redefinition cannot be completed - // due to the nature of the changes - List changePackets = classRedefinition.detectClassChanges(matchedInfos); - - for (ChangePacket packet : changePackets) { - JDWP.LOGGER.fine(() -> "Redefining class " + packet.info.getNewName()); - int result = classRedefinition.redefineClass(packet, refreshSubClasses); - if (result != 0) { - return result; - } - } - // refresh subclasses when needed - Collections.sort(refreshSubClasses, new SubClassHierarchyComparator()); - for (ObjectKlass subKlass : refreshSubClasses) { - JDWP.LOGGER.fine(() -> "Updating sub class " + subKlass.getName() + " for redefined super class"); - subKlass.onSuperKlassUpdate(); - } + // begin redefine transaction + ClassRedefinition.begin(); // redefine classes based on direct code changes first doRedefine(redefineInfos, changedKlasses); @@ -827,7 +805,7 @@ private void doRedefine(List redefineInfos, List chan Collections.sort(changePackets, new HierarchyComparator()); for (ChangePacket packet : changePackets) { - JDWP.LOGGER.fine(() -> "Redefining extra class " + packet.info.getNewName()); + JDWP.LOGGER.fine(() -> "Redefining class " + packet.info.getNewName()); int result = classRedefinition.redefineClass(packet, refreshSubClasses); if (result != 0) { throw new RedefintionNotSupportedException(result); From c2f1dbcc85c72d2369cbf4410af5a895e9e0f0eb Mon Sep 17 00:00:00 2001 From: "thomas.garcia" Date: Thu, 22 Apr 2021 15:41:24 +0200 Subject: [PATCH 080/290] Pass Xmx1g and Xms1g to awfy peak benchmarks. --- espresso/ci.jsonnet | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/espresso/ci.jsonnet b/espresso/ci.jsonnet index 9c8507425ba7..a43b1bdf3ddb 100644 --- a/espresso/ci.jsonnet +++ b/espresso/ci.jsonnet @@ -35,8 +35,8 @@ builds: common.builds + [ // Benchmarks // AWFY peak perf. benchmarks - common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*') + {name: 'weekly-bench-espresso-jvm-ce-awfy-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*') + {name: 'weekly-bench-espresso-native-ce-awfy-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-jdk8-linux-amd64'}, // AWFY interpreter benchmarks common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('jvm-ce', 'awfy:*') + {name: 'weekly-bench-espresso-jvm-ce-awfy_interpreter-jdk8-linux-amd64'}, From 0f233c1961f1efd3058ee06b20abfbfda8dad239 Mon Sep 17 00:00:00 2001 From: "thomas.garcia" Date: Fri, 7 May 2021 18:28:12 +0200 Subject: [PATCH 081/290] AWFY benchmark uses small heap value. --- compiler/ci_common/benchmark-suites.libsonnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ci_common/benchmark-suites.libsonnet b/compiler/ci_common/benchmark-suites.libsonnet index e9e4ec54337e..8c9374021ba2 100644 --- a/compiler/ci_common/benchmark-suites.libsonnet +++ b/compiler/ci_common/benchmark-suites.libsonnet @@ -28,7 +28,7 @@ // suite definitions // ***************** - awfy: cc.compiler_benchmark + c.heap.default + { + awfy: cc.compiler_benchmark + c.heap.small + { suite:: "awfy", run+: [ c.hwlocIfNuma(self.is_numa, $.mx_benchmark + ["awfy:*"] + $.bench_arguments, node=self.numa_nodes[0]) From f813596844b2ee60c7a6cfaf2195293dc181a40d Mon Sep 17 00:00:00 2001 From: Martin Entlicher Date: Fri, 7 May 2021 16:28:32 +0200 Subject: [PATCH 082/290] Provide frame slots always in the default scope implementation. (GR-31238) --- .../api/debug/test/DebugValueTest.java | 63 ++++++++------- .../test/VariablesScopeTest.java | 80 +++++++++---------- .../truffle/polyglot/LegacyDefaultScope.java | 39 +++++++-- .../truffle/polyglot/LegacyScopesBridge.java | 10 +++ 4 files changed, 113 insertions(+), 79 deletions(-) diff --git a/truffle/src/com.oracle.truffle.api.debug.test/src/com/oracle/truffle/api/debug/test/DebugValueTest.java b/truffle/src/com.oracle.truffle.api.debug.test/src/com/oracle/truffle/api/debug/test/DebugValueTest.java index 6fc72c39f24d..fd9c11529435 100644 --- a/truffle/src/com.oracle.truffle.api.debug.test/src/com/oracle/truffle/api/debug/test/DebugValueTest.java +++ b/truffle/src/com.oracle.truffle.api.debug.test/src/com/oracle/truffle/api/debug/test/DebugValueTest.java @@ -69,6 +69,8 @@ import com.oracle.truffle.api.debug.DebuggerSession; import com.oracle.truffle.api.debug.SourceElement; import com.oracle.truffle.api.debug.SuspendedEvent; +import com.oracle.truffle.api.frame.FrameSlot; +import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.InstrumentableNode; @@ -276,6 +278,7 @@ public void testValueAttributes() throws Throwable { assertEquals("property", attributesTOValue.getName()); // All false initially assertFalse(attributesTOValue.isReadable()); + assertEquals("", attributesTOValue.toDisplayString()); assertFalse(attributesTOValue.isWritable()); assertFalse(attributesTOValue.isInternal()); assertFalse(attributesTOValue.hasReadSideEffects()); @@ -372,16 +375,22 @@ protected CallTarget parse(TruffleLanguage.ParsingRequest request) throws Except final class TestRootNode extends RootNode { @Node.Child TestBody body = new TestBody(); + private final FrameSlot a; + private final FrameSlot b; + private final FrameSlot c; TestRootNode(TruffleLanguage language) { super(language); + a = getFrameDescriptor().addFrameSlot("a", FrameSlotKind.Object); + b = getFrameDescriptor().addFrameSlot("b", FrameSlotKind.Int); + c = getFrameDescriptor().addFrameSlot("c", FrameSlotKind.Object); } @Override public Object execute(VirtualFrame frame) { - if (frame.getArguments().length == 0) { - return getCallTarget().call(null, 11, new Object()); - } + frame.setObject(a, new ValueLanguageTest.NullObject()); + frame.setInt(b, 11); + frame.setObject(c, new Object()); return body.execute(frame); } } @@ -392,34 +401,30 @@ public Object execute(VirtualFrame frame) { tester.startEval(source); expectSuspended((SuspendedEvent event) -> { Iterator declaredValues = event.getTopStackFrame().getScope().getDeclaredValues().iterator(); - DebugValue arg0 = declaredValues.next(); - DebugValue arg1 = declaredValues.next(); - DebugValue arg2 = declaredValues.next(); + DebugValue var0 = declaredValues.next(); + DebugValue var1 = declaredValues.next(); + // var2 is not an interop value assertFalse(declaredValues.hasNext()); - assertFalse(arg0.isReadable()); - assertTrue(arg1.isReadable()); - assertFalse(arg2.isReadable()); - - assertEquals("", arg0.toDisplayString()); - assertNull(arg0.getProperties()); - assertNull(arg0.getMetaObject()); - assertFalse(arg0.isArray()); - assertFalse(arg0.isBoolean()); - assertFalse(arg0.isDate()); - assertFalse(arg0.isDuration()); - assertFalse(arg0.isInstant()); - assertFalse(arg0.isInternal()); - assertFalse(arg0.isMetaObject()); - assertFalse(arg0.isNull()); - assertFalse(arg0.isNumber()); - assertFalse(arg0.isReadable()); - assertFalse(arg0.isString()); - assertFalse(arg0.isTime()); - assertFalse(arg0.isTimeZone()); - - assertEquals("11", arg1.toDisplayString()); - assertEquals("", arg2.toDisplayString()); + assertTrue(var0.isReadable()); + assertTrue(var1.isReadable()); + + assertNull(var0.getProperties()); + assertFalse(var0.isArray()); + assertFalse(var0.isBoolean()); + assertFalse(var0.isDate()); + assertFalse(var0.isDuration()); + assertFalse(var0.isInstant()); + assertFalse(var0.isInternal()); + assertFalse(var0.isMetaObject()); + assertTrue(var0.isNull()); + assertFalse(var0.isNumber()); + assertTrue(var0.isReadable()); + assertFalse(var0.isString()); + assertFalse(var0.isTime()); + assertFalse(var0.isTimeZone()); + + assertEquals("11", var1.toDisplayString()); }); } expectDone(); diff --git a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/VariablesScopeTest.java b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/VariablesScopeTest.java index f1db0377940f..c0e7833a9f38 100644 --- a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/VariablesScopeTest.java +++ b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/VariablesScopeTest.java @@ -277,49 +277,43 @@ public void doTestScope(TruffleInstrument.Env env, Node node, VirtualFrame frame String scopeName = INTEROP.asString(INTEROP.toDisplayString(lexicalScope)); int line = node.getSourceSection().getStartLine(); - if (line == 1) { - assertEquals("Line = " + line + ", scope name: ", "local", scopeName); - assertEquals("Lexical arguments", 0, getKeySize(lexicalScope)); - assertEquals("Dunamic arguments", 2, getKeySize(dynamicScope)); - assertTrue("Argument 0: ", contains(dynamicScope, "0")); - assertTrue("Argument 1: ", contains(dynamicScope, "1")); - assertEquals("Argument 0: ", 4, read(dynamicScope, "0")); - assertEquals("Argument 1: ", 5, read(dynamicScope, "1")); - } else { - assertEquals("Line = " + line + ", scope name: ", "local", scopeName); - - // Lexical access: - TruffleObject vars = (TruffleObject) lexicalScope; - int numVars = nodeEnter ? 2 : 3; - int varSize = getKeySize(vars); - - assertEquals("Line = " + line + ", num vars:", numVars, varSize); - if (numVars >= 1) { - assertTrue("Var a: ", contains(vars, "a")); - assertTrue(isNull(read(vars, "a"))); - } - if (numVars >= 2) { - assertTrue("Var b: ", contains(vars, "b")); - assertTrue(isNull(read(vars, "b"))); - } - if (numVars >= 3) { - assertTrue("Var n: ", contains(vars, "n")); - assertTrue(isNull(read(vars, "n"))); - } - - // Dynamic access: - vars = (TruffleObject) dynamicScope; - numVars = nodeEnter ? 1 : 2; - varSize = getKeySize(vars); - assertEquals("Line = " + line + ", num vars:", numVars, varSize); - if (numVars >= 1) { - assertTrue("Var a: ", contains(vars, "a")); - assertEquals("Var a: ", 10, read(vars, "a")); - } - if (numVars >= 2) { - assertTrue("Var n: ", contains(vars, "n")); - assertEquals("Var n: ", 2, read(vars, "n")); - } + assertEquals("Line = " + line + ", scope name: ", "local", scopeName); + + // Lexical access: + TruffleObject vars = (TruffleObject) lexicalScope; + int numVars = nodeEnter ? (line == 1) ? 0 : 2 : 3; + int varSize = getKeySize(vars); + + assertEquals("Line = " + line + ", num vars:", numVars, varSize); + if (numVars >= 1) { + assertTrue("Var a: ", contains(vars, "a")); + assertTrue(isNull(read(vars, "a"))); + } + if (numVars >= 2) { + assertTrue("Var b: ", contains(vars, "b")); + assertTrue(isNull(read(vars, "b"))); + } + if (numVars >= 3) { + assertTrue("Var n: ", contains(vars, "n")); + assertTrue(isNull(read(vars, "n"))); + } + + // Dynamic access: + vars = (TruffleObject) dynamicScope; + numVars = (line == 1) ? (nodeEnter ? 0 : 3) : (nodeEnter ? 1 : 2); + varSize = getKeySize(vars); + assertEquals("Line = " + line + ", num vars:", numVars, varSize); + if (numVars >= 1) { + assertTrue("Var a: ", contains(vars, "a")); + assertEquals("Var a: ", 10, read(vars, "a")); + } + if (numVars >= 2) { + assertTrue("Var n: ", contains(vars, "n")); + assertEquals("Var n: ", 2, read(vars, "n")); + } + if (numVars >= 3) { + assertTrue("Var b: ", contains(vars, "b")); + assertEquals("Var b: ", true, read(vars, "b")); } if (line == 2) { doTestTopScope(env); diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LegacyDefaultScope.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LegacyDefaultScope.java index e3587ce83733..2db769078baf 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LegacyDefaultScope.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LegacyDefaultScope.java @@ -40,9 +40,7 @@ */ package com.oracle.truffle.polyglot; -import java.util.Collections; import java.util.Iterator; -import java.util.List; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.interop.InteropLibrary; @@ -52,6 +50,7 @@ import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import java.util.NoSuchElementException; /** * A default frame slot based implementation of variables contained in the (default) frame scope. @@ -59,15 +58,14 @@ @SuppressWarnings("deprecation") abstract class LegacyDefaultScope implements Iterable { - private List scopeList; + private com.oracle.truffle.api.Scope scope; @Override public Iterator iterator() { - List list = scopeList; - if (list == null) { - scopeList = list = Collections.singletonList(createScope()); + if (scope == null) { + scope = createScope(); } - return list.iterator(); + return new LegacyScopeIterator(scope); } abstract com.oracle.truffle.api.Scope createScope(); @@ -130,4 +128,31 @@ boolean isMemberReadable(@SuppressWarnings("unused") String member) { return false; } } + + static final class LegacyScopeIterator implements Iterator { + + private com.oracle.truffle.api.Scope scope; + + LegacyScopeIterator(com.oracle.truffle.api.Scope scope) { + this.scope = scope; + } + + public boolean hasNext() { + return scope != null; + } + + public com.oracle.truffle.api.Scope next() { + com.oracle.truffle.api.Scope s = scope; + if (s != null) { + scope = null; + return s; + } else { + throw new NoSuchElementException(); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LegacyScopesBridge.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LegacyScopesBridge.java index 9aa0ab7ad094..9962085cb21d 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LegacyScopesBridge.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LegacyScopesBridge.java @@ -273,6 +273,16 @@ static Object legacyScopes2ScopeObject(NodeInterface node, Iterator scopesList = new ArrayList<>(5); if (node instanceof InstrumentableNode && ((InstrumentableNode) node).hasTag(StandardTags.RootTag.class)) { // Provide the arguments From 1c3f19b8fd0c73298486f7b2c47428a8268c00cc Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Fri, 7 May 2021 10:32:15 -0700 Subject: [PATCH 083/290] All built-in packages must be registered before information is used the first time --- .../svm/core/jdk/PlatformNativeLibrarySupport.java | 8 ++++++++ .../oracle/svm/hosted/SecurityServicesFeature.java | 8 ++++---- .../oracle/svm/hosted/jdk/JNIRegistrationAwt.java | 14 +++++--------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/PlatformNativeLibrarySupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/PlatformNativeLibrarySupport.java index cb5e89d2f03e..2176f9857a0f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/PlatformNativeLibrarySupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/PlatformNativeLibrarySupport.java @@ -35,6 +35,8 @@ import org.graalvm.nativeimage.impl.InternalPlatform; import org.graalvm.word.PointerBase; +import com.oracle.svm.core.util.VMError; + public abstract class PlatformNativeLibrarySupport { public static final String[] defaultBuiltInLibraries = { @@ -105,12 +107,18 @@ public boolean isBuiltinLibrary(@SuppressWarnings("unused") String name) { } private List builtInPkgNatives; + private boolean builtInPkgNativesSealed; public void addBuiltinPkgNativePrefix(String name) { + if (builtInPkgNativesSealed) { + throw VMError.shouldNotReachHere("Cannot register any more packages as built-ins because information has already been used."); + } builtInPkgNatives.add(name); } public boolean isBuiltinPkgNative(String name) { + builtInPkgNativesSealed = true; + String commonPrefix = "Java_"; if (name.startsWith(commonPrefix)) { String strippedName = name.substring(commonPrefix.length()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java index b1f93079272f..6c7817c8967a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java @@ -235,10 +235,14 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { access.registerReachabilityHandler(SecurityServicesFeature::linkSunEC, method(access, "sun.security.ec.ECDSASignature", "signDigest", byte[].class, byte[].class, byte[].class, byte[].class, int.class), method(access, "sun.security.ec.ECDSASignature", "verifySignedDigest", byte[].class, byte[].class, byte[].class, byte[].class)); + /* Ensure native calls to sun_security_ec* will be resolved as builtIn. */ + PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_security_ec"); } if (isPosix()) { access.registerReachabilityHandler(SecurityServicesFeature::linkJaas, method(access, "com.sun.security.auth.module.UnixSystem", "getUnixInfo")); + /* Resolve calls to com_sun_security_auth_module_UnixSystem* as builtIn. */ + PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("com_sun_security_auth_module_UnixSystem"); } } @@ -247,8 +251,6 @@ private static void linkSunEC(DuringAnalysisAccess a) { /* We statically link sunec thus we classify it as builtIn library */ PlatformNativeLibrarySupport.singleton(); NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("sunec"); - /* and ensure native calls to sun_security_ec* will be resolved as builtIn. */ - PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_security_ec"); nativeLibraries.addStaticJniLibrary("sunec"); if (isPosix()) { @@ -263,8 +265,6 @@ private static void linkJaas(DuringAnalysisAccess a) { NativeLibraries nativeLibraries = ((DuringAnalysisAccessImpl) a).getNativeLibraries(); /* We can statically link jaas, thus we classify it as builtIn library */ NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary(JavaVersionUtil.JAVA_SPEC >= 11 ? "jaas" : "jaas_unix"); - /* Resolve calls to com_sun_security_auth_module_UnixSystem* as builtIn. */ - PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("com_sun_security_auth_module_UnixSystem"); nativeLibraries.addStaticJniLibrary("jaas"); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAwt.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAwt.java index dd434171f8b5..600d2420e108 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAwt.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAwt.java @@ -59,9 +59,13 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { clazz(access, "sun.awt.X11GraphicsEnvironment"), clazz(access, "sun.font.FontManagerNativeLibrary"), clazz(access, "sun.java2d.Disposer")); + PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("java_awt"); + PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_awt"); + PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_java2d"); access.registerReachabilityHandler(JNIRegistrationAwt::registerFreeType, clazz(access, "sun.font.FontManagerNativeLibrary")); + PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_font"); access.registerReachabilityHandler(JNIRegistrationAwt::registerLCMS, clazz(access, "sun.java2d.cmm.lcms.LCMS")); @@ -73,6 +77,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { clazz(access, "sun.awt.image.JPEGImageDecoder"), clazz(access, "com.sun.imageio.plugins.jpeg.JPEGImageReader"), clazz(access, "com.sun.imageio.plugins.jpeg.JPEGImageWriter")); + PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("com_sun_imageio_plugins_jpeg"); access.registerReachabilityHandler(JNIRegistrationAwt::registerColorProfiles, clazz(access, "java.awt.color.ICC_Profile")); @@ -114,10 +119,6 @@ private static void handlePreferencesClassReachable(DuringAnalysisAccess access) nativeLibraries.addDynamicNonJniLibrary("Xi"); } - PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("java_awt"); - PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_awt"); - PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_java2d"); - nativeLibraries.addDynamicNonJniLibrary("stdc++"); nativeLibraries.addDynamicNonJniLibrary("m"); @@ -126,9 +127,6 @@ private static void handlePreferencesClassReachable(DuringAnalysisAccess access) } private static void registerJPEG(DuringAnalysisAccess access) { - - PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("com_sun_imageio_plugins_jpeg"); - NativeLibraries nativeLibraries = getNativeLibraries(access); NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("javajpeg"); @@ -156,8 +154,6 @@ private static void registerFreeType(DuringAnalysisAccess access) { nativeLibraries.addStaticJniLibrary("fontmanager", isHeadless() ? "awt_headless" : "awt_xawt"); nativeLibraries.addStaticJniLibrary("harfbuzz"); - PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_font"); - nativeLibraries.addDynamicNonJniLibrary("freetype"); JNIRuntimeAccess.register(clazz(access, "sun.font.FontConfigManager$FontConfigInfo")); From 1248d07b3a1d958e2a3446747b91864f82994512 Mon Sep 17 00:00:00 2001 From: stepan Date: Fri, 7 May 2021 17:25:04 +0200 Subject: [PATCH 084/290] SDK: report resources exhausted and any other unknown exceptions in launcher --- .../src/org/graalvm/launcher/LanguageLauncherBase.java | 10 ++++++++-- .../src/org/graalvm/launcher/Launcher.java | 4 ++-- .../api/test/polyglot/PolyglotExceptionTest.java | 1 + .../oracle/truffle/polyglot/PolyglotExceptionImpl.java | 9 ++++++++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/LanguageLauncherBase.java b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/LanguageLauncherBase.java index 3ce81af837d3..d6c500a76968 100644 --- a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/LanguageLauncherBase.java +++ b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/LanguageLauncherBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -148,8 +148,14 @@ protected boolean parseCommonOption(String defaultOptionPrefix, Map iterator = e.getPolyglotStackTrace().iterator(); boolean foundFrame = false; while (iterator.hasNext()) { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java index 59955b21142a..611da2ac4615 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java @@ -194,7 +194,14 @@ final class PolyglotExceptionImpl extends AbstractExceptionImpl { if (internal) { this.message = exception.toString(); } else { - this.message = exception.getMessage(); + String exceptionMessage = exception.getMessage(); + if (exceptionMessage != null) { + this.message = exceptionMessage; + } else if (resourceExhausted) { + this.message = "Resources exhausted"; + } else { + this.message = null; + } } } From c344a81625e2e6b39779f34efafd062116c268d9 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 10 May 2021 11:08:11 +0200 Subject: [PATCH 085/290] fix merge bug in fetching already loaded inner classes --- .../truffle/espresso/redefinition/InnerClassRedefiner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java index 9c53d1753ac4..405577ab6393 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/InnerClassRedefiner.java @@ -343,7 +343,7 @@ public void onKlassDefined(ObjectKlass objectKlass) { for (Klass loadedKlass : loadedKlasses) { if (loadedKlass instanceof ObjectKlass) { ObjectKlass objectKlass = (ObjectKlass) loadedKlass; - Matcher matcher = ANON_INNER_CLASS_PATTERN.matcher(klass.getNameAsString()); + Matcher matcher = ANON_INNER_CLASS_PATTERN.matcher(loadedKlass.getNameAsString()); if (matcher.matches()) { Symbol outerClassName = getOuterClassName(loadedKlass.getName()); if (outerClassName != null && outerClassName.length() > 0) { From 521bde3045ebde7adb842fbc5610f5be8f22fb74 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 10 May 2021 11:20:19 +0200 Subject: [PATCH 086/290] No need to have a fallback to lookup scope provider node from root node --- .../truffle/espresso/jdwp/api/CallFrame.java | 14 +------------ .../espresso/jdwp/api/JDWPContext.java | 13 ------------ .../espresso/runtime/JDWPContextImpl.java | 20 ------------------- 3 files changed, 1 insertion(+), 46 deletions(-) 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 86ec88585d16..d1824060d032 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 @@ -26,7 +26,6 @@ import com.oracle.truffle.api.debug.DebugStackFrame; import com.oracle.truffle.api.debug.DebugValue; import com.oracle.truffle.api.frame.Frame; -import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.NodeLibrary; @@ -153,18 +152,7 @@ private Object getScope() { JDWP.LOGGER.warning(() -> "Unable to get scope for " + currentNode.getClass()); } } else { - // fallback to lookup scope provider node from the root node - InstrumentableNode scopeNode = context.getScopeProviderNode(rootNode, frame); - if (scopeNode == null) { - JDWP.LOGGER.warning(() -> "Unable to get instrumentable node for root " + rootNode); - } else { - try { - scope = NodeLibrary.getUncached().getScope(scopeNode, frame, true); - } catch (UnsupportedMessageException e) { - JDWP.LOGGER.warning(() -> "Unable to get scope for " + scopeNode.getClass()); - } - JDWP.LOGGER.warning(() -> "Unable to get scope for " + scopeNode.getClass()); - } + JDWP.LOGGER.warning(() -> "Unable to get scope for " + currentNode.getClass()); } return scope; } 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 ebba0d16af18..ac7f34679531 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 @@ -27,7 +27,6 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.Frame; -import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; @@ -492,16 +491,4 @@ public interface JDWPContext { */ long getBCI(Node rawNode, Frame frame); - /** - * Returns a node that returns true for - * {@link com.oracle.truffle.api.interop.NodeLibrary#hasScope(Object, Frame)} or - * null if not present for this root node. The returned node must implement - * {@link InstrumentableNode} and return true for {@link InstrumentableNode#isInstrumentable()} - * - * @param rootNode the root node - * @param frame - * @return the nearest instrumentable node or null - */ - InstrumentableNode getScopeProviderNode(RootNode rootNode, Frame frame); - } 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 12d99a083455..bad0f8849fc1 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 @@ -34,10 +34,7 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.frame.Frame; -import com.oracle.truffle.api.instrumentation.InstrumentableNode; -import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode; import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.NodeLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; @@ -677,23 +674,6 @@ public Class> getLanguageClass() { return EspressoLanguage.class; } - @Override - public InstrumentableNode getScopeProviderNode(RootNode rootNode, Frame frame) { - if (rootNode instanceof EspressoRootNode) { - EspressoRootNode espressoRootNode = (EspressoRootNode) rootNode; - Node methodNode = espressoRootNode.getMethodNode(); - if (methodNode instanceof WrapperNode) { - methodNode = ((WrapperNode) methodNode).getDelegateNode(); - } - if (methodNode instanceof InstrumentableNode && ((InstrumentableNode) methodNode).isInstrumentable()) { - if (NodeLibrary.getUncached().hasScope(methodNode, frame)) { - return (InstrumentableNode) methodNode; - } - } - } - return null; - } - private static BciProvider getBciProviderNode(Node node) { if (node instanceof BciProvider) { return (BciProvider) node; From d1eaf77205efdf307620d26534490f38d8632114 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 10 May 2021 09:23:24 +0000 Subject: [PATCH 087/290] [GR-19768] Update Truffle import. PullRequest: fastr/2611 --- vm/mx.vm/suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index 2c9d85635a91..3b9f0585d295 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -66,7 +66,7 @@ }, { "name": "fastr", - "version": "f43a8fd365d039929886aacfe29c6d4150bd1ecf", + "version": "06b64a9e52a132cb5bba39d7f683c7d3224cc58b", "dynamic": True, "urls": [ {"url": "https://github.com/oracle/fastr.git", "kind": "git"}, From 267cf1481cdc4019fb818612a4a7dbf918756182 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 10 May 2021 11:28:45 +0200 Subject: [PATCH 088/290] avoid passing bytecodeNode around explicitly and move getBytecodeNode to base node class to make sure call is not performed on a wrapper node --- .../espresso/nodes/BaseEspressoStatementNode.java | 8 +++++++- .../oracle/truffle/espresso/nodes/BytecodeNode.java | 8 ++++---- .../truffle/espresso/nodes/EspressoStatementNode.java | 11 ++--------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java index f5dcf69b4c59..d4999bde5e5b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BaseEspressoStatementNode.java @@ -57,7 +57,13 @@ public WrapperNode createWrapper(ProbeNode probe) { return new BaseEspressoStatementNodeWrapper(this, probe); } - public abstract BytecodeNode getBytecodeNode(); + public final BytecodeNode getBytecodeNode() { + Node parent = getParent(); + while (!(parent instanceof BytecodeNode) && parent != null) { + parent = parent.getParent(); + } + return (BytecodeNode) parent; + } @ExportMessage @SuppressWarnings("static-method") diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index a805956569ff..93c42454539d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -1297,7 +1297,7 @@ public InstrumentableNode materializeInstrumentableNodes(Set Date: Mon, 10 May 2021 12:04:21 +0200 Subject: [PATCH 089/290] Minor style changes --- .../src/com/oracle/svm/core/jdk/Target_java_lang_Module.java | 2 +- .../com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java index 7d0ec96861a2..6c593aa46b49 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java @@ -46,7 +46,7 @@ public InputStream getResourceAsStream(String name) { @TargetClass(className = "java.lang.Module", innerClass = "ReflectionData", onlyWith = JDK11OrLater.class) private static final class ReflectionData { - @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, isFinal = true, declClassName = "java.lang.WeakPairMap") + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, isFinal = true, declClassName = "java.lang.WeakPairMap") // static Target_java_lang_WeakPairMap, Boolean> uses; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java index 7688c2602e17..852046ae9c96 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_WeakPairMap.java @@ -26,7 +26,7 @@ import com.oracle.svm.core.annotate.TargetClass; -@TargetClass(className = "java.lang.WeakPairMap") +@TargetClass(className = "java.lang.WeakPairMap", onlyWith = JDK11OrLater.class) final class Target_java_lang_WeakPairMap { } From 9b7b0ae705224a75b53b4e10bd00dbda809b58b3 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Sun, 25 Apr 2021 22:30:24 +0200 Subject: [PATCH 090/290] remove accidental usage of non-public JDK API --- .../dsl/processor/java/transform/AbstractCodeWriter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java index 4ab016df08c2..68d60e64ed15 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java @@ -51,6 +51,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import javax.annotation.processing.FilerException; import javax.lang.model.element.AnnotationMirror; @@ -78,7 +79,6 @@ import com.oracle.truffle.dsl.processor.java.model.CodeTreeKind; import com.oracle.truffle.dsl.processor.java.model.CodeTypeElement; import com.oracle.truffle.dsl.processor.java.model.CodeVariableElement; -import com.sun.org.apache.xpath.internal.functions.Function; public abstract class AbstractCodeWriter extends CodeElementScanner { @@ -186,7 +186,7 @@ private String useImport(Element enclosedType, TypeMirror type, boolean rawType) } } - static class Foobar { + static class Foobar, BiFunction> { } From 52709cf1423e410da8a12f67442d369afd2863e6 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Thu, 29 Apr 2021 22:04:38 +0200 Subject: [PATCH 091/290] append devkit packages instead of overriding all other packages --- wasm/ci.jsonnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/ci.jsonnet b/wasm/ci.jsonnet index 8a69ae81d51d..62df4a504814 100644 --- a/wasm/ci.jsonnet +++ b/wasm/ci.jsonnet @@ -11,7 +11,7 @@ common.jdk8_gate_linux_wabt + common.gate_graalwasm_jvmci + common.amd64 + {environment+: {GATE_TAGS: 'build,wasmtest'}} + {name: 'gate-graalwasm-unittest-linux-amd64'}, common.jdk8_gate_linux_wabt_emsdk + common.gate_graalwasm_emsdk_jvmci + common.amd64 + {environment+: {GATE_TAGS: 'buildall,wasmextratest'}} + {name: 'gate-graalwasm-extra-unittest-linux-amd64'}, common.jdk8_gate_linux_wabt_emsdk + common.gate_graalwasm_emsdk_jvmci + common.amd64 + {environment+: {GATE_TAGS: 'buildall,wasmbenchtest'}} + {name: 'gate-graalwasm-benchtest-linux-amd64'}, - common.jdk8_gate_windows_wabt + common.gate_graalwasm_jvmci + common.amd64 + {environment+: {GATE_TAGS: 'build,wasmtest'}} + {name: 'gate-graalwasm-unittest-windows-amd64'} + common.devkits["windows-oraclejdk8"], + common.jdk8_gate_windows_wabt + common.gate_graalwasm_jvmci + common.amd64 + {environment+: {GATE_TAGS: 'build,wasmtest'}} + {name: 'gate-graalwasm-unittest-windows-amd64', packages+: common.devkits["windows-oraclejdk8"].packages}, common.jdk11_gate_linux_wabt + common.gate_graalwasm_jvmci + common.aarch64 + {environment+: {GATE_TAGS: 'build,wasmtest'}} + {name: 'gate-graalwasm-unittest-11-linux-aarch64'}, From a0b6829e743b7611bff12f4b410b0e2204e6d5a5 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Wed, 28 Apr 2021 20:41:02 +0200 Subject: [PATCH 092/290] suppressed deprecation warnings --- .../compiler/serviceprovider/GraalUnsafeAccess.java | 10 ++++++++++ .../jdk8/lambda/InnerClassLambdaMetafactory.java | 2 +- .../ConfigurableClassInitialization.java | 13 +++++-------- .../svm/test/TestClassInitializationMustBeSafe.java | 7 ++++++- .../oracle/truffle/api/library/LibraryFactory.java | 1 + 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalUnsafeAccess.java b/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalUnsafeAccess.java index 7e01382be16f..349913065235 100644 --- a/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalUnsafeAccess.java +++ b/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalUnsafeAccess.java @@ -66,4 +66,14 @@ public static Unsafe getUnsafe() { } return UNSAFE; } + + @SuppressWarnings("deprecation") // deprecated since JDK 15 + public static boolean shouldBeInitialized(Class c) { + return UNSAFE.shouldBeInitialized(c); + } + + @SuppressWarnings("deprecation") // deprecated since JDK 15 + public static void ensureClassInitialized(Class c) { + UNSAFE.ensureClassInitialized(c); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/agent/jdk8/lambda/InnerClassLambdaMetafactory.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/agent/jdk8/lambda/InnerClassLambdaMetafactory.java index c0994b53de63..e1b3255904da 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/agent/jdk8/lambda/InnerClassLambdaMetafactory.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/agent/jdk8/lambda/InnerClassLambdaMetafactory.java @@ -225,7 +225,7 @@ CallSite buildCallSite() throws LambdaConversionException { f.setAccessible(true); if (eagerlyInitialize) { - UNSAFE.ensureClassInitialized(innerClass); + GraalUnsafeAccess.ensureClassInitialized(innerClass); } MethodHandles.Lookup implLookup = ((MethodHandles.Lookup) f.get(null)); MethodHandle lambdaHandle = invokedType.parameterCount() == 0 diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java index 69394f445326..84892b50d334 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java @@ -54,7 +54,6 @@ import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaType; -import sun.misc.Unsafe; /** * The core class for deciding whether a class should be initialized during image building or class @@ -62,8 +61,6 @@ */ public class ConfigurableClassInitialization implements ClassInitializationSupport { - private static final Unsafe UNSAFE = GraalUnsafeAccess.getUnsafe(); - /** * Setup for class initialization: configured through features and command line input. It * represents the user desires about class initialization and helps in finding configuration @@ -173,7 +170,7 @@ public void maybeInitializeHosted(ResolvedJavaType type) { */ private InitKind ensureClassInitialized(Class clazz, boolean allowErrors) { try { - UNSAFE.ensureClassInitialized(clazz); + GraalUnsafeAccess.ensureClassInitialized(clazz); return InitKind.BUILD_TIME; } catch (NoClassDefFoundError ex) { if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { @@ -266,7 +263,7 @@ public void initializeAtRunTime(Class clazz, String reason) { setSubclassesAsRunTime(clazz); checkEagerInitialization(clazz); - if (!UNSAFE.shouldBeInitialized(clazz)) { + if (!GraalUnsafeAccess.shouldBeInitialized(clazz)) { throw UserError.abort("The class %1$s has already been initialized; it is too late to register %1$s for build-time initialization (%2$s). %3$s", clazz.getTypeName(), reason, classInitializationErrorMessage(clazz, "Try avoiding this conflict by avoiding to initialize the class that caused initialization of " + clazz.getTypeName() + @@ -386,7 +383,7 @@ public void rerunInitialization(Class clazz, String reason) { checkEagerInitialization(clazz); try { - UNSAFE.ensureClassInitialized(clazz); + GraalUnsafeAccess.ensureClassInitialized(clazz); } catch (Throwable ex) { throw UserError.abort(ex, "Class initialization failed for %s. The class is requested for re-running (reason: %s)", clazz.getTypeName(), reason); } @@ -512,7 +509,7 @@ public boolean checkDelayedInitialization() { */ Set> illegalyInitialized = new HashSet<>(); for (Map.Entry, InitKind> entry : classInitKinds.entrySet()) { - if (entry.getValue().isRunTime() && !UNSAFE.shouldBeInitialized(entry.getKey())) { + if (entry.getValue().isRunTime() && !GraalUnsafeAccess.shouldBeInitialized(entry.getKey())) { illegalyInitialized.add(entry.getKey()); } } @@ -577,7 +574,7 @@ InitKind computeInitKindAndMaybeInitializeClass(Class clazz, boolean memoize, } /* Well, and enums that got initialized while annotations are parsed. */ - if (clazz.isEnum() && !UNSAFE.shouldBeInitialized(clazz)) { + if (clazz.isEnum() && !GraalUnsafeAccess.shouldBeInitialized(clazz)) { if (memoize) { forceInitializeHosted(clazz, "enums referred in annotations must be initialized", false); } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/TestClassInitializationMustBeSafe.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/TestClassInitializationMustBeSafe.java index 18e28f6e37d4..07f45c092368 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/TestClassInitializationMustBeSafe.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/TestClassInitializationMustBeSafe.java @@ -451,7 +451,7 @@ private static void checkClasses(boolean checkSafeEarly, boolean checkSafeLate) errors.add(checkedClass.getName() + ": Wrongly named class (nameHasSafeEarly: " + nameHasSafeEarly + ", nameHasSafeLate: " + nameHasSafeLate + ", nameHasDelayed: " + nameHasDelayed); } else { - boolean initialized = !UnsafeAccess.UNSAFE.shouldBeInitialized(checkedClass); + boolean initialized = !shouldBeInitialized(checkedClass); if (nameHasDelayed && initialized) { errors.add(checkedClass.getName() + ": Check for MustBeDelayed failed"); @@ -470,6 +470,11 @@ private static void checkClasses(boolean checkSafeEarly, boolean checkSafeLate) } } + @SuppressWarnings("deprecation") + private static boolean shouldBeInitialized(Class c) { + return UnsafeAccess.UNSAFE.shouldBeInitialized(c); + } + @Override public void afterRegistration(AfterRegistrationAccess access) { RuntimeClassInitialization.initializeAtBuildTime(UnsafeAccess.class); diff --git a/truffle/src/com.oracle.truffle.api.library/src/com/oracle/truffle/api/library/LibraryFactory.java b/truffle/src/com.oracle.truffle.api.library/src/com/oracle/truffle/api/library/LibraryFactory.java index 0f717d8d2a3a..3764fcb3e647 100644 --- a/truffle/src/com.oracle.truffle.api.library/src/com/oracle/truffle/api/library/LibraryFactory.java +++ b/truffle/src/com.oracle.truffle.api.library/src/com/oracle/truffle/api/library/LibraryFactory.java @@ -329,6 +329,7 @@ public final T getUncached() { return dispatch; } + @SuppressWarnings("deprecation") private void ensureLibraryInitialized() { CompilerAsserts.neverPartOfCompilation(); /* From 354d435ef463b73cea09ba939cacc6edb39eb178 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Thu, 29 Apr 2021 15:48:35 +0200 Subject: [PATCH 093/290] update overlay --- graal-common.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graal-common.json b/graal-common.json index 7ba8561d5401..d0cb21fbc5e5 100644 --- a/graal-common.json +++ b/graal-common.json @@ -1,7 +1,7 @@ { "README": "This file contains definitions that are useful for the hocon and jsonnet CI files of the graal and graal-enterprise repositories.", "ci": { - "overlay": "c311988b6a5be40dd62ee88ed228e7901e2b4393" + "overlay": "46ebf5a0d27430e5a5179c3b3d0e42931acef324" }, "mx_version" : "HEAD" } From 6b7cda18134473609e01bc097e24cbdbb4a8bdff Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Thu, 29 Apr 2021 20:09:48 +0200 Subject: [PATCH 094/290] add a guard to every jsonnet defined builder to avoid running it if only *.md files are edited by a PR (GR-30750) --- ci.jsonnet | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ci.jsonnet b/ci.jsonnet index 1af3b04fe3a0..bef3305e0be7 100644 --- a/ci.jsonnet +++ b/ci.jsonnet @@ -9,10 +9,19 @@ local espresso = import 'espresso/ci.jsonnet'; # Sulong local sulong = import 'sulong/ci.jsonnet'; + +# Add a guard to `build` that prevents it from running in the gate +# for a PR that only touches *.md flles. +local add_markdown_guard(build) = build + { + guard+: { + excludes+: ["**.md"] + } +}; + { # ensure that entries in common.jsonnet can be resolved _checkCommon: (import 'common.jsonnet'), ci_resources:: (import 'ci-resources.libsonnet'), specVersion: "2", - builds: compiler.builds + wasm.builds + espresso.builds + sulong.builds + builds: [add_markdown_guard(b) for b in (compiler.builds + wasm.builds + espresso.builds + sulong.builds)] } From b779a686b84d744b0daacba93029827c8da33af2 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Thu, 29 Apr 2021 01:19:12 +0200 Subject: [PATCH 095/290] =?UTF-8?q?avoid=20issues=20with=20javac=20?= =?UTF-8?q?=E2=80=94release=20option=20-=20refer=20to=20proprietary=20java?= =?UTF-8?q?.awt.peer.ComponentPeer=20symbolically=20-=20do=20not=20subclas?= =?UTF-8?q?s=20FinalReference?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/java/lang/ref/PublicFinalReference.java | 15 ++++++++++++--- .../oracle/svm/core/jdk/JavaAWTSubstitutions.java | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/java/lang/ref/PublicFinalReference.java b/espresso/src/com.oracle.truffle.espresso/src/java/lang/ref/PublicFinalReference.java index 361be5b38cdd..07e96e5bd44a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/java/lang/ref/PublicFinalReference.java +++ b/espresso/src/com.oracle.truffle.espresso/src/java/lang/ref/PublicFinalReference.java @@ -32,13 +32,14 @@ * *

* This class is just a placeholder, not usable as-is. A modified version without the throwing - * static initializer is injected via {@link sun.misc.Unsafe#defineClass}. + * static initializer is injected via {@link sun.misc.Unsafe#defineClass}. The injected version + * subclasses {@link FinalReference}. * * @see Target_java_lang_ref_Reference * @see com.oracle.truffle.espresso.FinalizationFeature * @see EspressoReference */ -public abstract class PublicFinalReference extends FinalReference { +public abstract class PublicFinalReference { // BEGIN CUT static { @@ -48,7 +49,15 @@ public abstract class PublicFinalReference extends FinalReference { } // END CUT + @SuppressWarnings("unused") public PublicFinalReference(T referent, ReferenceQueue queue) { - super(referent, queue); + } + + public T get() { + throw new AssertionError("Forbidden class"); + } + + public void clear() { + throw new AssertionError("Forbidden class"); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaAWTSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaAWTSubstitutions.java index 5eb14a238bc6..92f1c635ea9d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaAWTSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaAWTSubstitutions.java @@ -567,11 +567,24 @@ static void initIDs() { } + /** + * This is necessary to workaround `javac` warnings that cannot be suppressed. For example: + * + *

+     * JavaAWTSubstitutions.java:574: warning: ComponentPeer is internal proprietary API and may be removed in a future release
+     *       private void nativeSetSource(java.awt.peer.ComponentPeer peer) {
+     *                                                 ^
+     * 
+ */ + @TargetClass(className = "java.awt.peer.ComponentPeer", onlyWith = IsHeadless.class) + static final class Target_java_awt_peer_ComponentPeer { + } + @TargetClass(className = "java.awt.AWTEvent", onlyWith = IsHeadless.class) static final class Target_java_awt_AWTEvent { @Substitute - private void nativeSetSource(java.awt.peer.ComponentPeer peer) { + private void nativeSetSource(Target_java_awt_peer_ComponentPeer peer) { throw new UnsupportedOperationException(); } } From 3f9b69f2752c44e4e960cc94bd2b1d60b875a770 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Fri, 30 Apr 2021 21:31:06 +0200 Subject: [PATCH 096/290] pass --add-exports in args file on Windows (GR-31100) --- sdk/mx.sdk/mx_sdk_vm_impl.py | 5 ++++- sdk/mx.sdk/vm/launcher_template.cmd | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/sdk/mx.sdk/mx_sdk_vm_impl.py b/sdk/mx.sdk/mx_sdk_vm_impl.py index ed6726c9b50c..71ce6add5ef2 100644 --- a/sdk/mx.sdk/mx_sdk_vm_impl.py +++ b/sdk/mx.sdk/mx_sdk_vm_impl.py @@ -1874,7 +1874,10 @@ def _get_launcher_args(): return '' def _get_add_exports(): - return self.subject.native_image_config.get_add_exports(_known_missing_jars) + res = self.subject.native_image_config.get_add_exports(_known_missing_jars) + if mx.is_windows(): + res = ' '.join(('"{}"'.format(a) for a in res.split())) + return res _template_subst = mx_subst.SubstitutionEngine(mx_subst.string_substitutions) _template_subst.register_no_arg('module_launcher', _is_module_launcher) diff --git a/sdk/mx.sdk/vm/launcher_template.cmd b/sdk/mx.sdk/vm/launcher_template.cmd index c543a8e29bbc..237295245db2 100644 --- a/sdk/mx.sdk/vm/launcher_template.cmd +++ b/sdk/mx.sdk/vm/launcher_template.cmd @@ -75,13 +75,17 @@ for %%a in (%args%) do ( ) set "module_launcher=" +:: The list of --add-exports can easily exceed the 8191 command +:: line character limit so pass them in a command line arguments file. if "%module_launcher%"=="True" ( set "app_path_arg=--module-path" - call :escape_args - for %%v in (!args!) do ( - call :unescape_arg %%v - set "jvm_args=!jvm_args! !arg!" + set exports_file="%location%.!basename!.exports" + if not exist "!exports_file!" ( + for %%a in () do ( + echo %%a >> "!exports_file!" + ) ) + set "jvm_args=!jvm_args! @!exports_file!" ) else ( set "app_path_arg=-cp" ) From 07e742f0e56e02aa4ca0309cf4f7b015d535700b Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Sun, 2 May 2021 12:53:14 +0200 Subject: [PATCH 097/290] remove deprecated instantiation of wrapper classes for primitives (#3073) --- .../hotspot/GraalHotSpotVMConfigAccess.java | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfigAccess.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfigAccess.java index 70c1fdfb407c..938f2ca5b124 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfigAccess.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfigAccess.java @@ -376,34 +376,23 @@ public T getFlag(String name, Class type) { /** * @see HotSpotVMConfigAccess#getFlag(String, Class, Object) */ - @SuppressWarnings("deprecation") public T getFlag(String name, Class type, T notPresent, boolean expectPresent) { if (expectPresent) { return getFlag(name, type); } + // Expecting flag not to be present if (Assertions.assertionsEnabled()) { // There's more overhead for checking unexpectedly - // present flag values due to the fact that a VM call - // not exposed by JVMCI is needed to determine whether - // a flag value is available. As such, only pay the - // overhead when running with assertions enabled. - T sentinel; - if (type == Boolean.class) { - sentinel = type.cast(new Boolean(false)); - } else if (type == Byte.class) { - sentinel = type.cast(new Byte((byte) 123)); - } else if (type == Integer.class) { - sentinel = type.cast(new Integer(1234567890)); - } else if (type == Long.class) { - sentinel = type.cast(new Long(1234567890987654321L)); - } else if (type == String.class) { - sentinel = type.cast(new String("1234567890987654321")); - } else { - throw new JVMCIError("Unsupported flag type: " + type.getName()); - } - T value = access.getFlag(name, type, sentinel); - if (value != sentinel) { + // present flag values due to the fact that private + // JVMCI method (i.e., jdk.vm.ci.hotspot.CompilerToVM.getFlagValue) + // is needed to determine whether a flag value is available. + // As such, only incur the overhead when running with assertions enabled. + try { + T value = access.getFlag(name, type, null); + // Flag value present -> fail recordError(name, unexpected, String.valueOf(value)); + } catch (JVMCIError e) { + // Flag value not present -> pass } } return access.getFlag(name, type, notPresent); From c3039795b44d9f04799600bb78164a2fe17de206 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Sun, 2 May 2021 12:52:31 +0200 Subject: [PATCH 098/290] adapt to VM symbol additions and changes --- .../graalvm/compiler/hotspot/GraalHotSpotVMConfig.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java index 7d8d02eb7a84..cf78100d2ae2 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java @@ -394,7 +394,7 @@ private int getJvmciJvmtiCapabilityOffset(String name) { public final int threadIsMethodHandleReturnOffset = getFieldOffset("JavaThread::_is_method_handle_return", Integer.class, "int"); public final int threadObjectResultOffset = getFieldOffset("JavaThread::_vm_result", Integer.class, "oop"); public final int jvmciCountersThreadOffset = getFieldOffset("JavaThread::_jvmci_counters", Integer.class, "jlong*"); - public final int jvmciReserved0Offset = getFieldOffset("JavaThread::_jvmci_reserved0", Integer.class, "intptr_t*", -1, JVMCI ? jvmciGE(JVMCI_21_1_b02) : false); + public final int jvmciReserved0Offset = getFieldOffset("JavaThread::_jvmci_reserved0", Integer.class, "intptr_t*", -1, JVMCI ? jvmciGE(JVMCI_21_1_b02) : JDK >= 17); public final int doingUnsafeAccessOffset = getFieldOffset("JavaThread::_doing_unsafe_access", Integer.class, "bool", Integer.MAX_VALUE, JVMCI || JDK >= 14); // @formatter:off @@ -663,8 +663,9 @@ private static String markWordField(String simpleName) { } else if (JDK < 16) { threadPollingPageOffset = getFieldOffset("Thread::_polling_page", Integer.class, "volatile void*"); } else { - // JDK-8253180 - threadPollingPageOffset = getFieldOffset("Thread::_poll_data", Integer.class, "SafepointMechanism::ThreadData") + + // JDK-8253180 & JDK-8265932 + String name = JDK == 16 ? "Thread::_poll_data" : "JavaThread::_poll_data"; + threadPollingPageOffset = getFieldOffset(name, Integer.class, "SafepointMechanism::ThreadData") + getFieldOffset("SafepointMechanism::ThreadData::_polling_page", Integer.class, "volatile uintptr_t"); } } @@ -818,7 +819,7 @@ public int threadTlabPfTopOffset() { public final long newMultiArrayOrNullAddress = getAddress("JVMCIRuntime::new_multi_array_or_null", 0L, JVMCI || JDK >= 12 || (!IS_OPENJDK && JDK == 11 && JDK_UPDATE >= 7)); public final long dynamicNewInstanceOrNullAddress = getAddress("JVMCIRuntime::dynamic_new_instance_or_null", 0L, JVMCI || JDK >= 12 || (!IS_OPENJDK && JDK == 11 && JDK_UPDATE >= 7)); - public final long invokeJavaMethodAddress = getAddress("JVMCIRuntime::invoke_static_method_one_arg", 0L, JVMCI ? jvmciGE(JVMCI_21_1_b02) : false); + public final long invokeJavaMethodAddress = getAddress("JVMCIRuntime::invoke_static_method_one_arg", 0L, JVMCI ? jvmciGE(JVMCI_21_1_b02) : JDK >= 17); public boolean areNullAllocationStubsAvailable() { return newInstanceOrNullAddress != 0L; From 0a6df89c40dae2f167fd8174bdfe9bd5ec76469b Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Sat, 8 May 2021 23:28:47 +0200 Subject: [PATCH 099/290] added missing requireConcealed attributes --- compiler/mx.compiler/suite.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py index c283f8f46599..b0fd28bff59e 100644 --- a/compiler/mx.compiler/suite.py +++ b/compiler/mx.compiler/suite.py @@ -1785,6 +1785,11 @@ "dependencies" : [ "org.graalvm.compiler.truffle.test", ], + "requiresConcealed" : { + "jdk.internal.vm.ci" : [ + "jdk.vm.ci.meta", + ], + }, "annotationProcessors" : [ "GRAAL_PROCESSOR", "truffle:TRUFFLE_DSL_PROCESSOR" From 7f77be7a2546dbf18a0fabfee82c9741d97b6ef5 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Thu, 1 Apr 2021 12:31:22 +0200 Subject: [PATCH 100/290] remove hard coded assumptions about mx build location and layout --- substratevm/mx.substratevm/mx_substratevm_benchmark.py | 4 +--- vm/mx.vm/mx_vm_benchmark.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/substratevm/mx.substratevm/mx_substratevm_benchmark.py b/substratevm/mx.substratevm/mx_substratevm_benchmark.py index 61b21bbbe75a..8a81d086c45f 100644 --- a/substratevm/mx.substratevm/mx_substratevm_benchmark.py +++ b/substratevm/mx.substratevm/mx_substratevm_benchmark.py @@ -630,9 +630,7 @@ def successPatterns(self): @staticmethod def substitution_path(): - bench_suite = mx.suite('substratevm') - root_dir = mx.join(bench_suite.dir, 'mxbuild') - path = os.path.abspath(mx.join(root_dir, 'src', 'com.oracle.svm.bench', 'bin')) + path = mx.project('com.oracle.svm.bench').classpath_repr() if not mx.exists(path): mx.abort('Path to substitutions for scala dacapo not present: ' + path + '. Did you build all of substratevm?') return path diff --git a/vm/mx.vm/mx_vm_benchmark.py b/vm/mx.vm/mx_vm_benchmark.py index aec486ee712a..f4c5beb98c3a 100644 --- a/vm/mx.vm/mx_vm_benchmark.py +++ b/vm/mx.vm/mx_vm_benchmark.py @@ -134,7 +134,7 @@ def __init__(self, vm, bm_suite, args): self.stages = bm_suite.stages(args) self.last_stage = self.stages[-1] self.skip_agent_assertions = bm_suite.skip_agent_assertions(self.benchmark_name, args) - self.root_dir = self.benchmark_output_dir if self.benchmark_output_dir else mx.join(mx.suite('vm').dir, 'mxbuild') + self.root_dir = self.benchmark_output_dir if self.benchmark_output_dir else mx.suite('vm').get_output_root(platformDependent=False, jdkDependent=False) self.executable_suffix = ('-' + self.benchmark_name) if self.benchmark_name else '' self.executable, self.classpath_arguments, self.system_properties, self.image_run_args = NativeImageVM.extract_benchmark_arguments(args) self.executable_name = (os.path.splitext(os.path.basename(self.executable[1]))[0] + self.executable_suffix if self.executable[0] == '-jar' else self.executable[0] + self.executable_suffix).lower() From c948d66b4078f5e8cdae28135d2fbb4966dfd75b Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Wed, 28 Apr 2021 15:35:23 +0200 Subject: [PATCH 101/290] reduce gate noise for DynamicConstantTest --- .../core/test/DynamicConstantTest.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DynamicConstantTest.java b/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DynamicConstantTest.java index 091aea862879..acfac27b1242 100644 --- a/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DynamicConstantTest.java +++ b/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DynamicConstantTest.java @@ -30,7 +30,6 @@ import java.util.List; import java.util.Map; -import org.graalvm.compiler.java.BytecodeParser.BytecodeParserError; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.junit.Assume; import org.junit.Test; @@ -40,6 +39,7 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; +import jdk.vm.ci.meta.ConstantPool; import jdk.vm.ci.meta.ResolvedJavaMethod; /** @@ -240,20 +240,30 @@ protected byte[] generateClass(String internalClassName) { @SuppressWarnings("try") @Test public void test() throws Throwable { + boolean jvmciCompatibilityChecked = false; for (TestGenerator e : generators.values()) { Class testClass = getClass(e.className); ResolvedJavaMethod run = getResolvedJavaMethod(testClass, "run"); - try { - Result actual = executeActual(run, null); - if (VERBOSE) { - System.out.println(run.format("%H.%n(%p)") + " -> " + actual); - } - } catch (BytecodeParserError err) { - Throwable cause = err.getCause(); - Assume.assumeFalse("running on JVMCI that does not support CONSTANT_Dynamic", String.valueOf(cause).contains("Unknown JvmConstant tag 17")); - throw err; + if (!jvmciCompatibilityChecked) { + checkJVMCICompatibility(run); + jvmciCompatibilityChecked = true; + } + Result actual = executeActual(run, null); + if (VERBOSE) { + System.out.println(run.format("%H.%n(%p)") + " -> " + actual); } test(run, null); } } + + private static void checkJVMCICompatibility(ResolvedJavaMethod run) { + ConstantPool cp = run.getConstantPool(); + for (int i = 0; i < cp.length(); i++) { + try { + cp.lookupConstant(i); + } catch (Throwable t) { + Assume.assumeFalse("running on JVMCI that does not support CONSTANT_Dynamic", String.valueOf(t).contains("Unknown JvmConstant tag 17")); + } + } + } } From dc66d1d684e4971d51a72aafe1df0de805196a84 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Wed, 5 May 2021 16:10:23 +0200 Subject: [PATCH 102/290] dump phases as a tree for -Dgraal.ShowConfiguration=verbose --- .../compiler/core/common/util/PhasePlan.java | 122 ++++++++++++++++++ .../hotspot/CompilerConfigurationFactory.java | 39 ++---- .../compiler/lir/phases/LIRPhaseSuite.java | 11 +- .../graalvm/compiler/phases/PhaseSuite.java | 16 ++- 4 files changed, 154 insertions(+), 34 deletions(-) create mode 100644 compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/PhasePlan.java diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/PhasePlan.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/PhasePlan.java new file mode 100644 index 000000000000..382261fbfbec --- /dev/null +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/PhasePlan.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 org.graalvm.compiler.core.common.util; + +import java.util.Formatter; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * An ordered list of compiler phases. + * + * @param the type of a phase in the plan + */ +public interface PhasePlan { + + /** + * Gets an unmodifiable view on the phases in this plan. + */ + List getPhases(); + + /** + * Gets a name for a phase in the plan. + * + * This should be in the format of a qualified Java class name. + */ + default String getPhaseName(T phase) { + return phase.getClass().getName(); + } + + /** + * Utility for formatting a plan as a string. + */ + final class Printer { + private static final String CONNECTING_INDENT = "\u2502 "; // "| " + private static final String EMPTY_INDENT = " "; + private static final String CHILD = "\u251c\u2500\u2500 "; // "|-- " + private static final String LAST_CHILD = "\u2514\u2500\u2500 "; // "`-- " + + final Map abbreviations = new HashMap<>(); + + /** + * Prints {@code plan} to {@code buf}. + * + * @return {@code buf} + */ + public Formatter printTo(PhasePlan plan, Formatter buf) { + return printPlan(Printer.CHILD, Printer.LAST_CHILD, plan, buf); + } + + /** + * Prints {@code plan} to a string and returns it. + */ + public String toString(PhasePlan plan) { + return printPlan(Printer.CHILD, Printer.LAST_CHILD, plan, new Formatter()).toString(); + } + + @SuppressWarnings("unchecked") + private Formatter printPlan(String indent, String indentLast, PhasePlan plan, Formatter buf) { + for (Iterator iter = plan.getPhases().iterator(); iter.hasNext();) { + T phase = iter.next(); + String className = plan.getPhaseName(phase); + boolean hasNext = iter.hasNext(); + buf.format("%s%s%n", hasNext ? indent : indentLast, abbreviate(className)); + if (phase instanceof PhasePlan) { + PhasePlan subPlan = (PhasePlan) phase; + String prefix = hasNext ? CONNECTING_INDENT : EMPTY_INDENT; + printPlan(prefix + indent, prefix + indentLast, subPlan, buf); + } + } + return buf; + } + + private static int firstCapitalAfterPeriod(String s) { + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i - 1) == '.' && Character.isUpperCase(s.charAt(i))) { + return i; + } + } + return 0; + } + + private String abbreviate(String className) { + String abbreviation = abbreviations.get(className); + if (abbreviation == null) { + int simpleClassNameStart = firstCapitalAfterPeriod(className); + String simpleClassName = className.substring(simpleClassNameStart); + String packageName = simpleClassNameStart != 0 ? className.substring(0, simpleClassNameStart - 1) : ""; + if (abbreviations.values().contains(simpleClassName)) { + abbreviation = simpleClassName + " [" + packageName + "]"; + } else { + abbreviation = simpleClassName; + } + abbreviations.put(className, abbreviation); + } + return abbreviation; + } + } +} diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java index 19a3be2c50e8..b7eca31797d5 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java @@ -27,7 +27,6 @@ import static jdk.vm.ci.common.InitTimer.timer; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -35,18 +34,15 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.core.Instrumentation; import org.graalvm.compiler.core.common.SuppressFBWarnings; +import org.graalvm.compiler.core.common.util.PhasePlan; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.debug.TTY; -import org.graalvm.compiler.lir.phases.LIRPhase; -import org.graalvm.compiler.lir.phases.LIRPhaseSuite; import org.graalvm.compiler.options.EnumOptionKey; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionStability; import org.graalvm.compiler.options.OptionType; import org.graalvm.compiler.options.OptionValues; -import org.graalvm.compiler.phases.BasePhase; -import org.graalvm.compiler.phases.PhaseSuite; import org.graalvm.compiler.phases.tiers.CompilerConfiguration; import org.graalvm.compiler.serviceprovider.GraalServices; @@ -238,13 +234,12 @@ public static CompilerConfigurationFactory selectFactory(String name, OptionValu case verbose: { printConfigInfo(factory); CompilerConfiguration config = factory.createCompilerConfiguration(); - TTY.println("High tier: " + phaseNames(config.createHighTier(options))); - TTY.println("Mid tier: " + phaseNames(config.createMidTier(options))); - TTY.println("Low tier: " + phaseNames(config.createLowTier(options))); - TTY.println("Pre regalloc stage: " + phaseNames(config.createPreAllocationOptimizationStage(options))); - TTY.println("Regalloc stage: " + phaseNames(config.createAllocationStage(options))); - TTY.println("Post regalloc stage: " + phaseNames(config.createPostAllocationOptimizationStage(options))); - config.createAllocationStage(options); + printPlan("High tier:", config.createHighTier(options)); + printPlan("Mid tier:", config.createMidTier(options)); + printPlan("Low tier:", config.createLowTier(options)); + printPlan("Pre regalloc stage:", config.createPreAllocationOptimizationStage(options)); + printPlan("Regalloc stage:", config.createAllocationStage(options)); + printPlan("Post regalloc stage:", config.createPostAllocationOptimizationStage(options)); break; } } @@ -257,23 +252,7 @@ private static void printConfigInfo(CompilerConfigurationFactory factory) { TTY.printf("Using compiler configuration '%s' provided by %s loaded from %s%n", factory.name, factory.getClass().getName(), location); } - private static List phaseNames(PhaseSuite suite) { - Collection> phases = suite.getPhases(); - List res = new ArrayList<>(phases.size()); - for (BasePhase phase : phases) { - res.add(phase.contractorName()); - } - Collections.sort(res); - return res; - } - - private static List phaseNames(LIRPhaseSuite suite) { - List> phases = suite.getPhases(); - List res = new ArrayList<>(phases.size()); - for (LIRPhase phase : phases) { - res.add(phase.getClass().getName()); - } - Collections.sort(res); - return res; + static void printPlan(String label, PhasePlan plan) { + TTY.printf("%s%n%s", label, new PhasePlan.Printer().toString(plan)); } } diff --git a/compiler/src/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhaseSuite.java b/compiler/src/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhaseSuite.java index 92edd361033d..503a6b4bbe38 100644 --- a/compiler/src/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhaseSuite.java +++ b/compiler/src/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/LIRPhaseSuite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ -29,11 +29,12 @@ import java.util.List; import java.util.ListIterator; +import org.graalvm.compiler.core.common.util.PhasePlan; import org.graalvm.compiler.lir.gen.LIRGenerationResult; import jdk.vm.ci.code.TargetDescription; -public class LIRPhaseSuite extends LIRPhase { +public class LIRPhaseSuite extends LIRPhase implements PhasePlan> { private List> phases; private boolean immutable; @@ -44,6 +45,7 @@ public LIRPhaseSuite() { /** * Gets an unmodifiable view on the phases in this suite. */ + @Override public List> getPhases() { return Collections.unmodifiableList(phases); } @@ -115,4 +117,9 @@ public synchronized void setImmutable() { immutable = true; } } + + @Override + public String toString() { + return String.format("%s:%n%s", getClass().getSimpleName(), new PhasePlan.Printer().toString(this)); + } } diff --git a/compiler/src/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/PhaseSuite.java b/compiler/src/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/PhaseSuite.java index 86dbd7e81391..4d8ae92cd578 100644 --- a/compiler/src/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/PhaseSuite.java +++ b/compiler/src/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/PhaseSuite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, 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 @@ -29,12 +29,13 @@ import java.util.List; import java.util.ListIterator; +import org.graalvm.compiler.core.common.util.PhasePlan; import org.graalvm.compiler.nodes.StructuredGraph; /** * A compiler phase that can apply an ordered collection of phases to a graph. */ -public class PhaseSuite extends BasePhase { +public class PhaseSuite extends BasePhase implements PhasePlan> { private List> phases; private boolean immutable; @@ -100,10 +101,21 @@ public ListIterator> findLastPhase() { /** * Gets an unmodifiable view on the phases in this suite. */ + @Override public List> getPhases() { return Collections.unmodifiableList(phases); } + @Override + public String getPhaseName(BasePhase phase) { + return phase.contractorName(); + } + + @Override + public String toString() { + return String.format("%s:%n%s", getClass().getSimpleName(), new PhasePlan.Printer().toString(this)); + } + /** * Returns a {@link ListIterator} at the position of the first phase which is an instance of * {@code phaseClass} or null if no such phase can be found. From ea7b5ab96b07cd2e2c6f2825d94b2c03937ba290 Mon Sep 17 00:00:00 2001 From: stepan Date: Mon, 12 Apr 2021 17:27:05 +0200 Subject: [PATCH 103/290] Add regression test for race condition in DSL generated code --- .../truffle/api/dsl/CachedDataRaceTest.java | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java diff --git a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java new file mode 100644 index 000000000000..0e0b82ade230 --- /dev/null +++ b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.api.dsl; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +import org.junit.Test; + +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +/** + * Regression test for GR-30689. Tries to trigger race condition between a read of the field storied + * the CachedLibrary in the generated "execute" method and write of that field in the generated + * "executeAndSpecialize" method. Note that the race was reproducible only on JDK8. + */ +public class CachedDataRaceTest { + private static final int TEST_REPETITIONS = 5000; + + public abstract static class TestNodeBase extends Node { + public abstract Object execute(Object obj); + } + + public abstract static class TestCachedLibNode extends TestNodeBase { + @Specialization(limit = "20") + protected Object readDirect(Object obj, + @CachedLibrary("obj") InteropLibrary dylib) { + return dylib.isBoolean(obj); + } + } + + public static final class CachedNode extends Node { + public final Object field; + public final Object another; + + public CachedNode(Object field, Object another) { + this.field = field; + this.another = another; + } + } + + public abstract static class TestCachedNodeNode extends TestNodeBase { + @Specialization(guards = "node.field == obj", limit = "20") + protected Object readDirect(Object obj, + @Cached("new(obj, obj)") CachedNode node) { + return obj == node.another; + } + } + + @Test + public void testCachedLibraryDataRace() throws Exception { + testCachedDataRace(CachedDataRaceTestFactory.TestCachedLibNodeGen::create); + } + + @Test + public void testCachedNodeDataRace() throws Exception { + testCachedDataRace(CachedDataRaceTestFactory.TestCachedNodeNodeGen::create); + } + + public void testCachedDataRace(Supplier nodeFactory) throws Exception { + Object[] items = new Object[]{1, 1.2, 1L, "string", new Object(), true, (byte) 1, (short) 2, (float) 1.2, 'a'}; + int threadsCount = Runtime.getRuntime().availableProcessors(); + ExecutorService executorService = Executors.newFixedThreadPool(threadsCount); + Future[] futures = new Future[threadsCount]; + + for (int i = 0; i < TEST_REPETITIONS; i++) { + TestNodeBase node = nodeFactory.get(); + AtomicBoolean done = new AtomicBoolean(); + + // "reader" threads keep on reading the CachedLibrary until done + for (int idx = 0; idx < futures.length - 1; idx++) { + futures[idx] = executorService.submit(() -> { + while (!done.get()) { + node.execute(42); + } + }); + } + + // "writer" thread creates new CachedLibrary instances, then sets done + futures[futures.length - 1] = executorService.submit(() -> { + for (Object item : items) { + node.execute(item); + } + done.set(true); + }); + + for (Future future : futures) { + future.get(); + } + } + } +} From 1745ed876f962e130265c1ff70246a9ecd8f4ecc Mon Sep 17 00:00:00 2001 From: stepan Date: Fri, 16 Apr 2021 18:04:58 +0200 Subject: [PATCH 104/290] Fix DSL cached data race with memory fence in executeAndSpecialize --- .../com/oracle/truffle/api/dsl/CachedDataRaceTest.java | 2 +- .../com/oracle/truffle/dsl/processor/TruffleTypes.java | 2 ++ .../dsl/processor/generator/FlatNodeGenFactory.java | 8 ++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java index 0e0b82ade230..090dbe2f7033 100644 --- a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java +++ b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java @@ -53,7 +53,7 @@ import com.oracle.truffle.api.nodes.Node; /** - * Regression test for GR-30689. Tries to trigger race condition between a read of the field storied + * Regression test for GR-30689. Tries to trigger race condition between a read of the field storing * the CachedLibrary in the generated "execute" method and write of that field in the generated * "executeAndSpecialize" method. Note that the race was reproducible only on JDK8. */ diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleTypes.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleTypes.java index 5b47eb20c6af..6b8dd35a638b 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleTypes.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleTypes.java @@ -105,6 +105,7 @@ public class TruffleTypes { public static final String UnexpectedResultException_Name = "com.oracle.truffle.api.nodes.UnexpectedResultException"; public static final String VirtualFrame_Name = "com.oracle.truffle.api.frame.VirtualFrame"; public static final String HostLanguage_Name = "com.oracle.truffle.polyglot.HostLanguage"; + public static final String MemoryFence_Name = "com.oracle.truffle.api.memory.MemoryFence"; public final DeclaredType Assumption = c.getDeclaredType(Assumption_Name); public final DeclaredType CompilerAsserts = c.getDeclaredType(CompilerAsserts_Name); @@ -136,6 +137,7 @@ public class TruffleTypes { public final DeclaredType UnexpectedResultException = c.getDeclaredType(UnexpectedResultException_Name); public final DeclaredType VirtualFrame = c.getDeclaredType(VirtualFrame_Name); public final DeclaredType HostLanguage = c.getDeclaredTypeOptional(HostLanguage_Name); + public final DeclaredType MemoryFence = c.getDeclaredTypeOptional(MemoryFence_Name); // DSL API public static final String Bind_Name = "com.oracle.truffle.api.dsl.Bind"; diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/FlatNodeGenFactory.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/FlatNodeGenFactory.java index e81853516ee1..39ddb8542b73 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/FlatNodeGenFactory.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/FlatNodeGenFactory.java @@ -4104,6 +4104,14 @@ private Collection persistSpecializationClass(FrameState frameState, S CodeTree ref = var.createReference(); CodeTreeBuilder builder = new CodeTreeBuilder(null); + // We need to insert memory fence if there are cached values and those are stored in a + // linked list. Another thread may be traversing the linked list while we are updating it + // here: we must ensure that the item that is being appended to the list is fully + // initialized. + builder.startStatement(); + builder.startStaticCall(context.getTypes().MemoryFence, "storeStore"); + builder.end(); + builder.end(); builder.startStatement(); builder.string("this.", createSpecializationFieldName(specialization)); builder.string(" = "); From 0e89a68b1131ce4ad20c6bc8157420476ea9e06b Mon Sep 17 00:00:00 2001 From: stepan Date: Thu, 29 Apr 2021 10:30:21 +0200 Subject: [PATCH 105/290] CachedDataRaceTest: fewer repetitions & upper bound for the threads count --- .../src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java index 090dbe2f7033..54bb8b25cac9 100644 --- a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java +++ b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java @@ -58,7 +58,9 @@ * "executeAndSpecialize" method. Note that the race was reproducible only on JDK8. */ public class CachedDataRaceTest { - private static final int TEST_REPETITIONS = 5000; + private static final int TEST_REPETITIONS = 1000; + // Don't go too crazy, if JVM reports lots of available threads + private static final int MAX_THREADS = 8; public abstract static class TestNodeBase extends Node { public abstract Object execute(Object obj); @@ -102,7 +104,7 @@ public void testCachedNodeDataRace() throws Exception { public void testCachedDataRace(Supplier nodeFactory) throws Exception { Object[] items = new Object[]{1, 1.2, 1L, "string", new Object(), true, (byte) 1, (short) 2, (float) 1.2, 'a'}; - int threadsCount = Runtime.getRuntime().availableProcessors(); + int threadsCount = Math.min(MAX_THREADS, Runtime.getRuntime().availableProcessors()); ExecutorService executorService = Executors.newFixedThreadPool(threadsCount); Future[] futures = new Future[threadsCount]; From 7829f371fe5ed18a20f2fb54dd7c95dbce1c52d3 Mon Sep 17 00:00:00 2001 From: stepan Date: Thu, 29 Apr 2021 16:23:37 +0200 Subject: [PATCH 106/290] CachedDataRaceTest: gracefully shutdown the executor service --- .../truffle/api/dsl/CachedDataRaceTest.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java index 54bb8b25cac9..eb58acd35254 100644 --- a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java +++ b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java @@ -43,6 +43,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -102,15 +103,26 @@ public void testCachedNodeDataRace() throws Exception { testCachedDataRace(CachedDataRaceTestFactory.TestCachedNodeNodeGen::create); } - public void testCachedDataRace(Supplier nodeFactory) throws Exception { - Object[] items = new Object[]{1, 1.2, 1L, "string", new Object(), true, (byte) 1, (short) 2, (float) 1.2, 'a'}; + public static void testCachedDataRace(Supplier nodeFactory) throws Exception { int threadsCount = Math.min(MAX_THREADS, Runtime.getRuntime().availableProcessors()); ExecutorService executorService = Executors.newFixedThreadPool(threadsCount); + AtomicBoolean done = new AtomicBoolean(); + try { + testCachedDataRace(nodeFactory, executorService, threadsCount, done); + } finally { + done.set(true); + executorService.shutdown(); + executorService.awaitTermination(10000, TimeUnit.MILLISECONDS); + } + } + + private static void testCachedDataRace(Supplier nodeFactory, ExecutorService executorService, int threadsCount, AtomicBoolean done) throws Exception { + Object[] items = new Object[]{1, 1.2, 1L, "string", new Object(), true, (byte) 1, (short) 2, (float) 1.2, 'a'}; Future[] futures = new Future[threadsCount]; for (int i = 0; i < TEST_REPETITIONS; i++) { TestNodeBase node = nodeFactory.get(); - AtomicBoolean done = new AtomicBoolean(); + done.set(false); // "reader" threads keep on reading the CachedLibrary until done for (int idx = 0; idx < futures.length - 1; idx++) { From a26d874a4c29e6171a446c9764ca2f2a60969e45 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 10 May 2021 14:20:45 +0200 Subject: [PATCH 107/290] remove duplicate interface --- .../espresso/impl/ClassRegistries.java | 12 ++++---- .../redefinition/ClassLoadListener.java | 29 ------------------- .../impl/RedefinitionPluginHandler.java | 6 ++-- 3 files changed, 9 insertions(+), 38 deletions(-) delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java index 24b6bbfe0b21..78eab436e96d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java @@ -39,7 +39,7 @@ import com.oracle.truffle.espresso.descriptors.Symbol.Type; import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.meta.Meta; -import com.oracle.truffle.espresso.redefinition.ClassLoadListener; +import com.oracle.truffle.espresso.redefinition.DefineKlassListener; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; import com.oracle.truffle.espresso.substitutions.Host; @@ -62,7 +62,7 @@ public final class ClassRegistries { // specify it as volatile. private int totalClassLoadersSet = 0; - private ClassLoadListener classLoadListener; + private DefineKlassListener defineKlassListener; public ClassRegistries(EspressoContext context) { this.context = context; @@ -301,14 +301,14 @@ int[] aliveLoaders() { return loaders; } - public void registerListener(ClassLoadListener listener) { - this.classLoadListener = listener; + public void registerListener(DefineKlassListener listener) { + this.defineKlassListener = listener; } @TruffleBoundary public void onKlassDefined(ObjectKlass klass) { - if (classLoadListener != null) { - classLoadListener.onClassLoad(klass); + if (defineKlassListener != null) { + defineKlassListener.onKlassDefined(klass); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java deleted file mode 100644 index 4523cbc58c38..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassLoadListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.redefinition; - -import com.oracle.truffle.espresso.impl.ObjectKlass; - -public interface ClassLoadListener { - void onClassLoad(ObjectKlass klass); -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java index 33d6dbb14e54..9ec7ae60c288 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -36,13 +36,13 @@ import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; -import com.oracle.truffle.espresso.redefinition.ClassLoadListener; +import com.oracle.truffle.espresso.redefinition.DefineKlassListener; import com.oracle.truffle.espresso.redefinition.plugins.api.ClassLoadAction; import com.oracle.truffle.espresso.redefinition.plugins.api.InternalRedefinitionPlugin; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; -public final class RedefinitionPluginHandler implements RedefineListener, ClassLoadListener { +public final class RedefinitionPluginHandler implements RedefineListener, DefineKlassListener { private final EspressoContext context; private final Set internalPlugins = Collections.synchronizedSet(new HashSet<>(1)); @@ -95,7 +95,7 @@ private void activatePlugin(InternalRedefinitionPlugin plugin) { @TruffleBoundary @Override - public void onClassLoad(ObjectKlass klass) { + public void onKlassDefined(ObjectKlass klass) { // internal plugins Symbol type = klass.getType(); // fire registered load actions From 7eee498f8a54f52361012800c654a34aeab6f22e Mon Sep 17 00:00:00 2001 From: Jakub Chaloupka Date: Wed, 5 May 2021 11:55:38 +0200 Subject: [PATCH 108/290] Add PolyglotProxy to polyglot context heap boundaries. --- .../api/test/polyglot/ProxyLanguage.java | 6 +- .../polyglot/TestProxyObjectNotReachable.java | 191 ++++++++++++++++++ .../polyglot/ObjectSizeCalculator.java | 1 + 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/TestProxyObjectNotReachable.java diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxyLanguage.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxyLanguage.java index e6f28f57372a..775bf76b7654 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxyLanguage.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxyLanguage.java @@ -113,7 +113,11 @@ public static ProxyLanguage getCurrentLanguage() { protected LanguageContext createContext(com.oracle.truffle.api.TruffleLanguage.Env env) { if (wrapper) { delegate.languageInstance = this; - return delegate.createContext(env); + LanguageContext c = delegate.createContext(env); + if (delegate.onCreate != null) { + delegate.onCreate.accept(c); + } + return c; } else { LanguageContext c = new LanguageContext(env); if (onCreate != null) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/TestProxyObjectNotReachable.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/TestProxyObjectNotReachable.java new file mode 100644 index 000000000000..181ffa8e5f6e --- /dev/null +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/TestProxyObjectNotReachable.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.api.test.polyglot; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleOptions; +import com.oracle.truffle.api.impl.DefaultTruffleRuntime; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; + +public class TestProxyObjectNotReachable extends AbstractPolyglotTest { + @ExportLibrary(InteropLibrary.class) + static class ScopeObject implements TruffleObject { + final Map members = Collections.synchronizedMap(new LinkedHashMap<>()); + + ScopeObject() { + } + + @ExportMessage + boolean isScope() { + return true; + } + + @ExportMessage + boolean hasMembers() { + return true; + } + + @SuppressWarnings("static-method") + @ExportMessage + final Object getMembers(@SuppressWarnings("unused") boolean includeInternal) { + return null; + } + + @SuppressWarnings("static-method") + @ExportMessage + final boolean isMemberReadable(@SuppressWarnings("unused") String member) { + return false; + } + + @SuppressWarnings("static-method") + @ExportMessage + Object readMember(@SuppressWarnings("unused") String key) { + return null; + } + + @ExportMessage + @CompilerDirectives.TruffleBoundary + Object writeMember(String key, Object value) { + members.put(key, value); + return value; + } + + @SuppressWarnings("static-method") + @ExportMessage + final boolean isMemberModifiable(@SuppressWarnings("unused") String member) { + return true; + } + + @SuppressWarnings("static-method") + @ExportMessage + final boolean isMemberInsertable(@SuppressWarnings("unused") String member) { + return true; + } + + @SuppressWarnings("static-method") + @ExportMessage + boolean hasLanguage() { + return true; + } + + @ExportMessage + Class> getLanguage() { + return LanguageWithScope.class; + } + + @ExportMessage + @CompilerDirectives.TruffleBoundary + Object toDisplayString(@SuppressWarnings("unused") boolean config) { + return "global"; + } + + } + + static class LanguageWithScopeContext extends ProxyLanguage.LanguageContext { + ScopeObject scope = new ScopeObject(); + + LanguageWithScopeContext(TruffleLanguage.Env env) { + super(env); + } + } + + static class LanguageWithScope extends ProxyLanguage { + @Override + protected LanguageContext createContext(Env env) { + return new LanguageWithScopeContext(env); + } + + @Override + protected Object getScope(LanguageContext languageContext) { + return ((LanguageWithScopeContext) languageContext).scope; + } + } + + @Test + public void testRetainedSizeWithProxyObject() { + Assume.assumeFalse(TruffleOptions.AOT); + Assume.assumeFalse(Truffle.getRuntime() instanceof DefaultTruffleRuntime); + setupEnv(Context.newBuilder(), new LanguageWithScope()); + context.getBindings(ProxyLanguage.ID).putMember("proxyObject", new ProxyObject() { + @SuppressWarnings("unused") private final Context ctx = context; + + @Override + public Object getMember(String key) { + return null; + } + + @Override + public Object getMemberKeys() { + return null; + } + + @Override + public boolean hasMember(String key) { + return false; + } + + @Override + public void putMember(String key, Value value) { + + } + }); + long retainedSize = instrumentEnv.calculateContextHeapSize(instrumentEnv.getEnteredContext(), 16L * 1024L * 1024L, new AtomicBoolean(false)); + Assert.assertTrue(retainedSize > 0); + Assert.assertTrue(retainedSize < 16L * 1024L * 1024L); + } +} diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/ObjectSizeCalculator.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/ObjectSizeCalculator.java index fb6a6ad1e361..b213e42a6dfa 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/ObjectSizeCalculator.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/ObjectSizeCalculator.java @@ -275,6 +275,7 @@ private static boolean isContextHeapBoundary(Object obj) { (obj instanceof HostFunction) || (obj instanceof HostException) || (obj instanceof HostLanguage.HostContext) || + (obj instanceof PolyglotProxy) || (obj instanceof Class) || (obj instanceof OptionValues) || From 4e5f7b830e743d3152bb69794207e700d0f07352 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Mon, 10 May 2021 14:58:37 +0200 Subject: [PATCH 109/290] bump timeout for graalDaily and graalWeekly jobs to 1:30:00 --- compiler/ci_common/gate.hocon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/ci_common/gate.hocon b/compiler/ci_common/gate.hocon index 7772f30b2063..5b4e244b502d 100644 --- a/compiler/ci_common/gate.hocon +++ b/compiler/ci_common/gate.hocon @@ -3,11 +3,11 @@ gateCmd : ["mx", "--strict-compliance"] ${gateCmdSuffix} graalWeekly: ${graal-weekly-notifications} { targets: ["weekly"] - timelimit: "45:00" + timelimit: "1:30:00" } graalDaily: ${graal-weekly-notifications} { targets: ["daily"] - timelimit: "45:00" + timelimit: "1:30:00" } gateTest : { environment : { From 3974499ff4ec8343219a484fde479bb8d6fe3182 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Thu, 18 Mar 2021 16:52:53 +0200 Subject: [PATCH 110/290] Refactor: Make fields of debugentry.Range final --- .../oracle/objectfile/debugentry/Range.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 9834420f2915..d2060e4afd62 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -35,22 +35,22 @@ public class Range { private static final String CLASS_DELIMITER = "."; private FileEntry fileEntry; - private String className; - private String methodName; - private String symbolName; - private String paramSignature; - private String returnTypeName; - private String fullMethodName; - private String fullMethodNameWithParams; - private int lo; - private int hi; - private int line; - private boolean isDeoptTarget; - private int modifiers; + private final String className; + private final String methodName; + private final String symbolName; + private final String paramSignature; + private final String returnTypeName; + private final String fullMethodName; + private final String fullMethodNameWithParams; + private final int lo; + private final int hi; + private final int line; + private final boolean isDeoptTarget; + private final int modifiers; /* * This is null for a primary range. */ - private Range primary; + private final Range primary; /* * Create a primary range. From 5fabaa6b1de7dd7d478289f5c68c6d99ac3ff236 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Thu, 18 Mar 2021 16:53:34 +0200 Subject: [PATCH 111/290] Refactor: Make debugentry.Range.fileEntry final --- .../src/com/oracle/objectfile/debugentry/Range.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index d2060e4afd62..6657acb965da 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -34,7 +34,7 @@ public class Range { private static final String CLASS_DELIMITER = "."; - private FileEntry fileEntry; + private final FileEntry fileEntry; private final String className; private final String methodName; private final String symbolName; @@ -184,10 +184,6 @@ public FileEntry getFileEntry() { return fileEntry; } - public void setFileEntry(FileEntry fileEntry) { - this.fileEntry = fileEntry; - } - public int getModifiers() { return modifiers; } From e361a1e61bed4d2fc535f4511d488c12630fd9b1 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 23 Mar 2021 13:28:41 +0200 Subject: [PATCH 112/290] Make ClassEntry.processMethod return the added MethodEntry --- .../com/oracle/objectfile/debugentry/ClassEntry.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 7dc4e567b0df..3df28e349181 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -267,9 +267,9 @@ private void processInterface(String interfaceName, DebugInfoBase debugInfoBase, interfaceClassEntry.addImplementor(this, debugContext); } - protected void processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { - String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); - String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.valueType()); + protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.methodName()); + String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.returnTypeName()); int modifiers = debugMethodInfo.modifiers(); List paramTypes = debugMethodInfo.paramTypes(); List paramNames = debugMethodInfo.paramNames(); @@ -294,7 +294,9 @@ protected void processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debu * substitution */ FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); - methods.add(new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers)); + final MethodEntry methodEntry = new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers); + methods.add(methodEntry); + return methodEntry; } @Override From 90c03c7a1bc454e5bda0a36cb37c39dddd073459 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 23 Mar 2021 13:56:44 +0200 Subject: [PATCH 113/290] Refactor: Restructure DebugMethodInfo, DebugCodeInfo and DebugLineInfo This allows code and line info to get access to the properties of the corresponding method. --- .../objectfile/debugentry/ClassEntry.java | 4 +- .../objectfile/debugentry/DebugInfoBase.java | 12 +-- .../debuginfo/DebugInfoProvider.java | 60 ++++-------- .../image/NativeImageDebugInfoProvider.java | 91 +++++++++++++++++-- 4 files changed, 109 insertions(+), 58 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 3df28e349181..a65cd25fdab6 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -268,8 +268,8 @@ private void processInterface(String interfaceName, DebugInfoBase debugInfoBase, } protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { - String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.methodName()); - String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.returnTypeName()); + String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); + String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.valueType()); int modifiers = debugMethodInfo.modifiers(); List paramTypes = debugMethodInfo.paramTypes(); List paramNames = debugMethodInfo.paramNames(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index 3a4117403b3e..805bcdea07e2 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -237,16 +237,16 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { String fileName = debugCodeInfo.fileName(); Path filePath = debugCodeInfo.filePath(); Path cachePath = debugCodeInfo.cachePath(); - String className = TypeEntry.canonicalize(debugCodeInfo.className()); - String methodName = debugCodeInfo.methodName(); + String className = TypeEntry.canonicalize(debugCodeInfo.ownerType()); + String methodName = debugCodeInfo.name(); String symbolName = debugCodeInfo.symbolNameForMethod(); String paramSignature = debugCodeInfo.paramSignature(); - String returnTypeName = TypeEntry.canonicalize(debugCodeInfo.returnTypeName()); + String returnTypeName = TypeEntry.canonicalize(debugCodeInfo.valueType()); int lo = debugCodeInfo.addressLo(); int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); boolean isDeoptTarget = debugCodeInfo.isDeoptTarget(); - int modifiers = debugCodeInfo.getModifiers(); + int modifiers = debugCodeInfo.modifiers(); /* Search for a method defining this primary range. */ ClassEntry classEntry = ensureClassEntry(className); @@ -257,8 +257,8 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> { String fileNameAtLine = debugLineInfo.fileName(); Path filePathAtLine = debugLineInfo.filePath(); - String classNameAtLine = TypeEntry.canonicalize(debugLineInfo.className()); - String methodNameAtLine = debugLineInfo.methodName(); + String classNameAtLine = TypeEntry.canonicalize(debugLineInfo.ownerType()); + String methodNameAtLine = debugLineInfo.name(); String symbolNameAtLine = debugLineInfo.symbolNameForMethod(); int loAtLine = lo + debugLineInfo.addressLo(); int hiAtLine = lo + debugLineInfo.addressHi(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index e98b94c817a8..4098643f6005 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -200,32 +200,33 @@ interface DebugFieldInfo extends DebugMemberInfo { int size(); } - interface DebugMethodInfo extends DebugMemberInfo { - List paramTypes(); - - List paramNames(); - } - - /** - * Access details of a specific compiled method. - */ - interface DebugCodeInfo extends DebugFileInfo { - void debugContext(Consumer action); + interface DebugMethodInfo extends DebugMemberInfo{ + /** + * @return a string identifying the method parameters. + */ + String paramSignature(); /** - * @return the fully qualified name of the class owning the compiled method. + * @return an array of Strings identifying the method parameters. */ - String className(); + List paramTypes(); /** - * @return the name of the compiled method including signature. + * @return an array of Strings with the method parameters' names. */ - String methodName(); + List paramNames(); /** * @return the symbolNameForMethod string */ String symbolNameForMethod(); + } + + /** + * Access details of a specific compiled method. + */ + interface DebugCodeInfo extends DebugMethodInfo { + void debugContext(Consumer action); /** * @return the lowest address containing code generated for the method represented as an @@ -250,16 +251,6 @@ interface DebugCodeInfo extends DebugFileInfo { */ Stream lineInfoProvider(); - /** - * @return a string identifying the method parameters. - */ - String paramSignature(); - - /** - * @return a string identifying the method return type. - */ - String returnTypeName(); - /** * @return the size of the method frame between prologue and epilogue. */ @@ -275,8 +266,6 @@ interface DebugCodeInfo extends DebugFileInfo { * @return true if this method has been compiled in as a deoptimization target */ boolean isDeoptTarget(); - - int getModifiers(); } /** @@ -302,22 +291,7 @@ interface DebugDataInfo { * Access details of code generated for a specific outer or inlined method at a given line * number. */ - interface DebugLineInfo extends DebugFileInfo { - /** - * @return the fully qualified name of the class owning the outer or inlined method. - */ - String className(); - - /** - * @return the name of the outer or inlined method including signature. - */ - String methodName(); - - /** - * @return the symbolNameForMethod string - */ - String symbolNameForMethod(); - + interface DebugLineInfo extends DebugMethodInfo { /** * @return the lowest address containing code generated for an outer or inlined code segment * reported at this line represented as an offset into the code segment. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index a644b10ec30e..01ad884bb7f2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -36,6 +36,7 @@ import java.util.function.Consumer; import java.util.stream.Stream; +import jdk.vm.ci.meta.JavaType; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.code.SourceMapping; import org.graalvm.compiler.core.common.CompressEncoding; @@ -250,6 +251,13 @@ private static ResolvedJavaType getOriginal(ResolvedJavaType javaType) { return null; } + private static String toJavaName(JavaType javaType) { + if (javaType instanceof HostedType) { + return getJavaType((HostedType) javaType, true).toJavaName(); + } + return javaType.toJavaName(); + } + private final Path cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); private abstract class NativeImageDebugFileInfo implements DebugFileInfo { @@ -679,6 +687,11 @@ public String valueType() { return hostedMethod.getSignature().getReturnType(null).toJavaName(); } + @Override + public String paramSignature() { + return hostedMethod.format("%P"); + } + @Override public List paramTypes() { LinkedList paramTypes = new LinkedList<>(); @@ -700,6 +713,11 @@ public List paramNames() { return paramNames; } + @Override + public String symbolNameForMethod() { + return NativeImage.localSymbolNameForMethod(hostedMethod); + } + @Override public int modifiers() { return hostedMethod.getModifiers(); @@ -858,12 +876,12 @@ public void debugContext(Consumer action) { } @Override - public String className() { + public String ownerType() { return getJavaType(hostedMethod, true).toJavaName(); } @Override - public String methodName() { + public String name() { ResolvedJavaMethod targetMethod = hostedMethod.getWrapped().getWrapped(); if (targetMethod instanceof SubstitutionMethod) { targetMethod = ((SubstitutionMethod) targetMethod).getOriginal(); @@ -894,7 +912,7 @@ public String paramSignature() { } @Override - public String returnTypeName() { + public String valueType() { return hostedMethod.format("%R"); } @@ -958,11 +976,33 @@ public List getFrameSizeChanges() { @Override public boolean isDeoptTarget() { - return methodName().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); + return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); + } + + @Override + public List paramTypes() { + LinkedList paramTypes = new LinkedList<>(); + Signature signature = hostedMethod.getSignature(); + for (int i = 0; i < signature.getParameterCount(false); i++) { + final JavaType parameterType = signature.getParameterType(i, null); + paramTypes.add(toJavaName(parameterType)); + } + return paramTypes; } @Override - public int getModifiers() { + public List paramNames() { + /* Can only provide blank names for now. */ + LinkedList paramNames = new LinkedList<>(); + Signature signature = hostedMethod.getSignature(); + for (int i = 0; i < signature.getParameterCount(false); i++) { + paramNames.add(""); + } + return paramNames; + } + + @Override + public int modifiers() { return hostedMethod.getModifiers(); } } @@ -1022,12 +1062,12 @@ public Path cachePath() { } @Override - public String className() { + public String ownerType() { return method.format("%H"); } @Override - public String methodName() { + public String name() { ResolvedJavaMethod targetMethod = method; while (targetMethod instanceof WrappedJavaMethod) { targetMethod = ((WrappedJavaMethod) targetMethod).getWrapped(); @@ -1057,6 +1097,16 @@ public String methodName() { return name; } + @Override + public String valueType() { + return method.format("%R"); + } + + @Override + public String paramSignature() { + return method.format("%P"); + } + @Override public String symbolNameForMethod() { return NativeImage.localSymbolNameForMethod(method); @@ -1081,6 +1131,33 @@ public int line() { return -1; } + @Override + public List paramTypes() { + LinkedList paramTypes = new LinkedList<>(); + Signature signature = method.getSignature(); + for (int i = 0; i < signature.getParameterCount(false); i++) { + final JavaType parameterType = signature.getParameterType(i, null); + paramTypes.add(toJavaName(parameterType)); + } + return paramTypes; + } + + @Override + public List paramNames() { + /* Can only provide blank names for now. */ + LinkedList paramNames = new LinkedList<>(); + Signature signature = method.getSignature(); + for (int i = 0; i < signature.getParameterCount(false); i++) { + paramNames.add(""); + } + return paramNames; + } + + @Override + public int modifiers() { + return method.getModifiers(); + } + @SuppressWarnings("try") private void computeFullFilePath() { ResolvedJavaType declaringClass = method.getDeclaringClass(); From b384283b5ac0e455e66648c0d661ce5410bdd863 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 23 Mar 2021 14:12:30 +0200 Subject: [PATCH 114/290] Associate Ranges with MethodEntries --- .../objectfile/debugentry/ClassEntry.java | 22 +++++-- .../objectfile/debugentry/DebugInfoBase.java | 12 ++-- .../oracle/objectfile/debugentry/Range.java | 60 ++++++++----------- .../elf/dwarf/DwarfInfoSectionImpl.java | 20 +++---- 4 files changed, 55 insertions(+), 59 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index a65cd25fdab6..05d1fd815082 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -337,10 +337,9 @@ public ClassEntry getSuperClass() { return superClass; } - public Range makePrimaryRange(String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, FileEntry primaryFileEntry, int lo, - int hi, int primaryLine, - int modifiers, boolean isDeoptTarget) { - FileEntry fileEntryToUse = primaryFileEntry; + public Range makePrimaryRange(String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, MethodEntry method, int lo, + int hi, int primaryLine, boolean isDeoptTarget) { + FileEntry fileEntryToUse = method.fileEntry; if (fileEntryToUse == null) { /* * Search for a matching method to supply the file entry or failing that use the one @@ -358,6 +357,19 @@ public Range makePrimaryRange(String methodName, String symbolName, String param fileEntryToUse = this.fileEntry; } } - return new Range(this.typeName, methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntryToUse, lo, hi, primaryLine, modifiers, isDeoptTarget); + return new Range(symbolName, stringTable, method, fileEntryToUse, lo, hi, primaryLine, isDeoptTarget); + } + + public MethodEntry ensureMethodEntry(DebugInfoProvider.DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); + String paramSignature = debugMethodInfo.paramSignature(); + String returnTypeName = debugMethodInfo.valueType(); + // TODO improve data structure to avoid loops... + for (MethodEntry methodEntry : methods) { + if (methodEntry.match(methodName, paramSignature, returnTypeName)) { + return methodEntry; + } + } + return processMethod(debugMethodInfo, debugInfoBase, debugContext); } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index 805bcdea07e2..fae22206ae67 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -236,7 +236,6 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { */ String fileName = debugCodeInfo.fileName(); Path filePath = debugCodeInfo.filePath(); - Path cachePath = debugCodeInfo.cachePath(); String className = TypeEntry.canonicalize(debugCodeInfo.ownerType()); String methodName = debugCodeInfo.name(); String symbolName = debugCodeInfo.symbolNameForMethod(); @@ -246,12 +245,11 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); boolean isDeoptTarget = debugCodeInfo.isDeoptTarget(); - int modifiers = debugCodeInfo.modifiers(); /* Search for a method defining this primary range. */ ClassEntry classEntry = ensureClassEntry(className); - FileEntry fileEntry = ensureFileEntry(fileName, filePath, cachePath); - Range primaryRange = classEntry.makePrimaryRange(methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntry, lo, hi, primaryLine, modifiers, isDeoptTarget); + MethodEntry methodEntry = classEntry.ensureMethodEntry(debugCodeInfo, this, debugContext); + Range primaryRange = classEntry.makePrimaryRange(methodName, symbolName, paramSignature, returnTypeName, stringTable, methodEntry, lo, hi, primaryLine, isDeoptTarget); debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", className, methodName, filePath, fileName, primaryLine, lo, hi); classEntry.indexPrimary(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> { @@ -263,13 +261,13 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { int loAtLine = lo + debugLineInfo.addressLo(); int hiAtLine = lo + debugLineInfo.addressHi(); int line = debugLineInfo.line(); - Path cachePathAtLine = debugLineInfo.cachePath(); /* * Record all subranges even if they have no line or file so we at least get a * symbol for them and don't see a break in the address range. */ - FileEntry subFileEntry = ensureFileEntry(fileNameAtLine, filePathAtLine, cachePathAtLine); - Range subRange = new Range(classNameAtLine, methodNameAtLine, symbolNameAtLine, stringTable, subFileEntry, loAtLine, hiAtLine, line, primaryRange); + ClassEntry subClassEntry = ensureClassEntry(classNameAtLine); + MethodEntry subMethodEntry = subClassEntry.ensureMethodEntry(debugLineInfo, this, debugContext); + Range subRange = new Range(symbolNameAtLine, stringTable, subMethodEntry, loAtLine, hiAtLine, line, primaryRange); classEntry.indexSubRange(subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", classNameAtLine, methodNameAtLine, filePathAtLine, fileNameAtLine, line, loAtLine, hiAtLine); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 6657acb965da..9bea361bef6a 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -35,18 +35,13 @@ public class Range { private static final String CLASS_DELIMITER = "."; private final FileEntry fileEntry; - private final String className; - private final String methodName; + private MethodEntry methodEntry; private final String symbolName; - private final String paramSignature; - private final String returnTypeName; - private final String fullMethodName; private final String fullMethodNameWithParams; private final int lo; private final int hi; private final int line; private final boolean isDeoptTarget; - private final int modifiers; /* * This is null for a primary range. */ @@ -55,41 +50,36 @@ public class Range { /* * Create a primary range. */ - public Range(String className, String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, FileEntry fileEntry, int lo, int hi, int line, - int modifiers, boolean isDeoptTarget) { - this(className, methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntry, lo, hi, line, modifiers, isDeoptTarget, null); + public Range(String symbolName, StringTable stringTable, MethodEntry methodEntry, FileEntry fileEntry, int lo, int hi, int line, + boolean isDeoptTarget) { + this(symbolName, stringTable, methodEntry, fileEntry, lo, hi, line, isDeoptTarget, null); } /* * Create a secondary range. */ - public Range(String className, String methodName, String symbolName, StringTable stringTable, FileEntry fileEntry, int lo, int hi, int line, - Range primary) { - this(className, methodName, symbolName, "", "", stringTable, fileEntry, lo, hi, line, 0, false, primary); + public Range(String symbolName, StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary) { + this(symbolName, stringTable, methodEntry, methodEntry.fileEntry, lo, hi, line, false, primary); } /* * Create a primary or secondary range. */ - private Range(String className, String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, FileEntry fileEntry, int lo, int hi, int line, - int modifiers, boolean isDeoptTarget, Range primary) { - this.fileEntry = fileEntry; + private Range(String symbolName, StringTable stringTable, MethodEntry methodEntry, FileEntry fileEntry, int lo, int hi, int line, + boolean isDeoptTarget, Range primary) { + this.fileEntry = fileEntry; // TODO remove and use fileEntry from MethodEntry if (fileEntry != null) { stringTable.uniqueDebugString(fileEntry.getFileName()); stringTable.uniqueDebugString(fileEntry.getPathName()); } - this.className = stringTable.uniqueString(className); - this.methodName = stringTable.uniqueString(methodName); + assert methodEntry != null; + this.methodEntry = methodEntry; this.symbolName = stringTable.uniqueString(symbolName); - this.paramSignature = stringTable.uniqueString(paramSignature); - this.returnTypeName = stringTable.uniqueString(returnTypeName); - this.fullMethodName = stringTable.uniqueString(constructClassAndMethodName()); this.fullMethodNameWithParams = stringTable.uniqueString(constructClassAndMethodNameWithParams()); this.lo = lo; this.hi = hi; this.line = line; this.isDeoptTarget = isDeoptTarget; - this.modifiers = modifiers; this.primary = primary; } @@ -106,11 +96,11 @@ public Range getPrimary() { } public String getClassName() { - return className; + return methodEntry.ownerType.typeName; } public String getMethodName() { - return methodName; + return methodEntry.memberName; } public String getSymbolName() { @@ -130,7 +120,7 @@ public int getLine() { } public String getFullMethodName() { - return fullMethodName; + return constructClassAndMethodName(); } public String getFullMethodNameWithParams() { @@ -143,23 +133,23 @@ public boolean isDeoptTarget() { private String getExtendedMethodName(boolean includeClass, boolean includeParams, boolean includeReturnType) { StringBuilder builder = new StringBuilder(); - if (includeReturnType && returnTypeName.length() > 0) { - builder.append(returnTypeName); + if (includeReturnType && methodEntry.valueType.typeName.length() > 0) { + builder.append(methodEntry.valueType.typeName); builder.append(' '); } - if (includeClass && className != null) { - builder.append(className); + if (includeClass && getClassName() != null) { + builder.append(getClassName()); builder.append(CLASS_DELIMITER); } - builder.append(methodName); + builder.append(getMethodName()); if (includeParams) { builder.append('('); - builder.append(paramSignature); + builder.append(String.join(", ", methodEntry.paramNames)); builder.append(')'); } if (includeReturnType) { builder.append(" "); - builder.append(returnTypeName); + builder.append(methodEntry.valueType.typeName); } return builder.toString(); } @@ -173,11 +163,11 @@ private String constructClassAndMethodNameWithParams() { } public String getMethodReturnTypeName() { - return returnTypeName; + return methodEntry.valueType.typeName; } - public String getParamSignature() { - return paramSignature; + public TypeEntry[] getParamTypes() { + return methodEntry.paramTypes; } public FileEntry getFileEntry() { @@ -185,7 +175,7 @@ public FileEntry getFileEntry() { } public int getModifiers() { - return modifiers; + return methodEntry.modifiers; } @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 4f501d4738dd..39ac113011f7 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -724,18 +724,14 @@ private int writeMethodParameterDeclarations(DebugContext context, ClassEntry cl if (!Modifier.isStatic(range.getModifiers())) { pos = writeMethodParameterDeclaration(context, "this", classEntry.getTypeName(), true, isSpecification, buffer, pos); } - String paramsString = range.getParamSignature(); - if (!paramsString.isEmpty()) { - String[] paramTypes = paramsString.split(","); - for (int i = 0; i < paramTypes.length; i++) { - String paramName = uniqueDebugString(""); - String paramTypeName = paramTypes[i].trim(); - FileEntry fileEntry = range.getFileEntry(); - if (fileEntry != null) { - pos = writeMethodParameterDeclaration(context, paramName, paramTypeName, false, isSpecification, buffer, pos); - } else { - pos = writeMethodParameterDeclaration(context, paramTypeName, paramTypeName, false, isSpecification, buffer, pos); - } + for (TypeEntry paramType : range.getParamTypes()) { + String paramTypeName = paramType.getTypeName(); + String paramName = uniqueDebugString(""); + FileEntry fileEntry = range.getFileEntry(); + if (fileEntry != null) { + pos = writeMethodParameterDeclaration(context, paramName, paramTypeName, false, isSpecification, buffer, pos); + } else { + pos = writeMethodParameterDeclaration(context, paramTypeName, paramTypeName, false, isSpecification, buffer, pos); } } return pos; From 8390fd26f3df037c0eef8f2784ff76e74b09b2fc Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 23 Mar 2021 14:23:12 +0200 Subject: [PATCH 115/290] Fix NativeImageDebugLineInfo.ownerType() to get original from substs --- .../svm/hosted/image/NativeImageDebugInfoProvider.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 01ad884bb7f2..df03832bbfd8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -1063,7 +1063,11 @@ public Path cachePath() { @Override public String ownerType() { - return method.format("%H"); + if (method instanceof HostedMethod) { + return getJavaType((HostedMethod) method, true).toJavaName(); + } else { + return method.getDeclaringClass().toJavaName(); + } } @Override From f485a3c15f5e29daf480657c0bfa22662f8005ae Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 23 Mar 2021 16:25:22 +0200 Subject: [PATCH 116/290] Refactor: Migrate isDeoptTarget to DebugMethodInfo And cache value in MethodEntry --- .../oracle/objectfile/debugentry/ClassEntry.java | 6 +++--- .../oracle/objectfile/debugentry/DebugInfoBase.java | 3 +-- .../oracle/objectfile/debugentry/MethodEntry.java | 8 +++++++- .../src/com/oracle/objectfile/debugentry/Range.java | 13 +++++-------- .../objectfile/debuginfo/DebugInfoProvider.java | 12 ++++++------ .../hosted/image/NativeImageDebugInfoProvider.java | 10 ++++++++++ 6 files changed, 32 insertions(+), 20 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 05d1fd815082..2974c262a089 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -294,7 +294,7 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa * substitution */ FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); - final MethodEntry methodEntry = new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers); + final MethodEntry methodEntry = new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers, debugMethodInfo.isDeoptTarget()); methods.add(methodEntry); return methodEntry; } @@ -338,7 +338,7 @@ public ClassEntry getSuperClass() { } public Range makePrimaryRange(String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, MethodEntry method, int lo, - int hi, int primaryLine, boolean isDeoptTarget) { + int hi, int primaryLine) { FileEntry fileEntryToUse = method.fileEntry; if (fileEntryToUse == null) { /* @@ -357,7 +357,7 @@ public Range makePrimaryRange(String methodName, String symbolName, String param fileEntryToUse = this.fileEntry; } } - return new Range(symbolName, stringTable, method, fileEntryToUse, lo, hi, primaryLine, isDeoptTarget); + return new Range(symbolName, stringTable, method, fileEntryToUse, lo, hi, primaryLine); } public MethodEntry ensureMethodEntry(DebugInfoProvider.DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index fae22206ae67..8e7da1ff2150 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -244,12 +244,11 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { int lo = debugCodeInfo.addressLo(); int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); - boolean isDeoptTarget = debugCodeInfo.isDeoptTarget(); /* Search for a method defining this primary range. */ ClassEntry classEntry = ensureClassEntry(className); MethodEntry methodEntry = classEntry.ensureMethodEntry(debugCodeInfo, this, debugContext); - Range primaryRange = classEntry.makePrimaryRange(methodName, symbolName, paramSignature, returnTypeName, stringTable, methodEntry, lo, hi, primaryLine, isDeoptTarget); + Range primaryRange = classEntry.makePrimaryRange(methodName, symbolName, paramSignature, returnTypeName, stringTable, methodEntry, lo, hi, primaryLine); debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", className, methodName, filePath, fileName, primaryLine, lo, hi); classEntry.indexPrimary(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java index 7b84995d6cd5..38f13550e807 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java @@ -29,13 +29,15 @@ public class MethodEntry extends MemberEntry { TypeEntry[] paramTypes; String[] paramNames; + final boolean isDeoptTarget; - public MethodEntry(FileEntry fileEntry, String methodName, ClassEntry ownerType, TypeEntry valueType, TypeEntry[] paramTypes, String[] paramNames, int modifiers) { + public MethodEntry(FileEntry fileEntry, String methodName, ClassEntry ownerType, TypeEntry valueType, TypeEntry[] paramTypes, String[] paramNames, int modifiers, boolean isDeoptTarget) { super(fileEntry, methodName, ownerType, valueType, modifiers); assert ((paramTypes == null && paramNames == null) || (paramTypes != null && paramNames != null && paramTypes.length == paramNames.length)); this.paramTypes = paramTypes; this.paramNames = paramNames; + this.isDeoptTarget = isDeoptTarget; } public String methodName() { @@ -94,4 +96,8 @@ public boolean match(String methodName, String paramSignature, String returnType } return true; } + + public boolean isDeoptTarget() { + return isDeoptTarget; + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 9bea361bef6a..a3d6e1c0db28 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -41,7 +41,6 @@ public class Range { private final int lo; private final int hi; private final int line; - private final boolean isDeoptTarget; /* * This is null for a primary range. */ @@ -50,23 +49,22 @@ public class Range { /* * Create a primary range. */ - public Range(String symbolName, StringTable stringTable, MethodEntry methodEntry, FileEntry fileEntry, int lo, int hi, int line, - boolean isDeoptTarget) { - this(symbolName, stringTable, methodEntry, fileEntry, lo, hi, line, isDeoptTarget, null); + public Range(String symbolName, StringTable stringTable, MethodEntry methodEntry, FileEntry fileEntry, int lo, int hi, int line) { + this(symbolName, stringTable, methodEntry, fileEntry, lo, hi, line, null); } /* * Create a secondary range. */ public Range(String symbolName, StringTable stringTable, MethodEntry methodEntry, int lo, int hi, int line, Range primary) { - this(symbolName, stringTable, methodEntry, methodEntry.fileEntry, lo, hi, line, false, primary); + this(symbolName, stringTable, methodEntry, methodEntry.fileEntry, lo, hi, line, primary); } /* * Create a primary or secondary range. */ private Range(String symbolName, StringTable stringTable, MethodEntry methodEntry, FileEntry fileEntry, int lo, int hi, int line, - boolean isDeoptTarget, Range primary) { + Range primary) { this.fileEntry = fileEntry; // TODO remove and use fileEntry from MethodEntry if (fileEntry != null) { stringTable.uniqueDebugString(fileEntry.getFileName()); @@ -79,7 +77,6 @@ private Range(String symbolName, StringTable stringTable, MethodEntry methodEntr this.lo = lo; this.hi = hi; this.line = line; - this.isDeoptTarget = isDeoptTarget; this.primary = primary; } @@ -128,7 +125,7 @@ public String getFullMethodNameWithParams() { } public boolean isDeoptTarget() { - return isDeoptTarget; + return methodEntry.isDeoptTarget; } private String getExtendedMethodName(boolean includeClass, boolean includeParams, boolean includeReturnType) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 4098643f6005..5c5729de8eb2 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -200,7 +200,7 @@ interface DebugFieldInfo extends DebugMemberInfo { int size(); } - interface DebugMethodInfo extends DebugMemberInfo{ + interface DebugMethodInfo extends DebugMemberInfo { /** * @return a string identifying the method parameters. */ @@ -220,6 +220,11 @@ interface DebugMethodInfo extends DebugMemberInfo{ * @return the symbolNameForMethod string */ String symbolNameForMethod(); + + /** + * @return true if this method has been compiled in as a deoptimization target + */ + boolean isDeoptTarget(); } /** @@ -261,11 +266,6 @@ interface DebugCodeInfo extends DebugMethodInfo { * to an empty frame */ List getFrameSizeChanges(); - - /** - * @return true if this method has been compiled in as a deoptimization target - */ - boolean isDeoptTarget(); } /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index df03832bbfd8..20e31c2bd8e8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -718,6 +718,11 @@ public String symbolNameForMethod() { return NativeImage.localSymbolNameForMethod(hostedMethod); } + @Override + public boolean isDeoptTarget() { + return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); + } + @Override public int modifiers() { return hostedMethod.getModifiers(); @@ -1116,6 +1121,11 @@ public String symbolNameForMethod() { return NativeImage.localSymbolNameForMethod(method); } + @Override + public boolean isDeoptTarget() { + return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); + } + @Override public int addressLo() { return lo; From 8500f4367ed7890ad3c177c22f211ff8237b1a67 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 23 Mar 2021 16:29:45 +0200 Subject: [PATCH 117/290] Refactor: Make MethodEntry fields final --- .../src/com/oracle/objectfile/debugentry/MethodEntry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java index 38f13550e807..5b0fcb1092a2 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java @@ -27,8 +27,8 @@ package com.oracle.objectfile.debugentry; public class MethodEntry extends MemberEntry { - TypeEntry[] paramTypes; - String[] paramNames; + final TypeEntry[] paramTypes; + final String[] paramNames; final boolean isDeoptTarget; public MethodEntry(FileEntry fileEntry, String methodName, ClassEntry ownerType, TypeEntry valueType, TypeEntry[] paramTypes, String[] paramNames, int modifiers, boolean isDeoptTarget) { From bdb9490d423856ec3dc722bf5c9ee192777d4f33 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 23 Mar 2021 16:36:34 +0200 Subject: [PATCH 118/290] Refactor: Avoid Qualified types in ClassEntry --- .../src/com/oracle/objectfile/debugentry/ClassEntry.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 2974c262a089..fa7df11c5625 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -26,7 +26,7 @@ package com.oracle.objectfile.debugentry; -import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFieldInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; @@ -300,7 +300,7 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa } @Override - protected FieldEntry addField(DebugInfoProvider.DebugFieldInfo debugFieldInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + protected FieldEntry addField(DebugFieldInfo debugFieldInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { FieldEntry fieldEntry = super.addField(debugFieldInfo, debugInfoBase, debugContext); FileEntry fieldFileEntry = fieldEntry.getFileEntry(); if (fieldFileEntry != null) { @@ -360,7 +360,7 @@ public Range makePrimaryRange(String methodName, String symbolName, String param return new Range(symbolName, stringTable, method, fileEntryToUse, lo, hi, primaryLine); } - public MethodEntry ensureMethodEntry(DebugInfoProvider.DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + public MethodEntry ensureMethodEntry(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); String paramSignature = debugMethodInfo.paramSignature(); String returnTypeName = debugMethodInfo.valueType(); From 70792d9ae44148d55d1611662964c48b7f132e0a Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 23 Mar 2021 20:36:34 +0200 Subject: [PATCH 119/290] Remove redundant lookup for fileEntry ClassEntry.makePrimaryRange(...) --- .../objectfile/debugentry/ClassEntry.java | 20 +++---------------- .../objectfile/debugentry/DebugInfoBase.java | 4 +--- .../oracle/objectfile/debugentry/Range.java | 2 +- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index fa7df11c5625..2bb7369844ff 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -337,25 +337,11 @@ public ClassEntry getSuperClass() { return superClass; } - public Range makePrimaryRange(String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, MethodEntry method, int lo, - int hi, int primaryLine) { + public Range makePrimaryRange(String symbolName, StringTable stringTable, MethodEntry method, int lo, int hi, int primaryLine) { FileEntry fileEntryToUse = method.fileEntry; if (fileEntryToUse == null) { - /* - * Search for a matching method to supply the file entry or failing that use the one - * from this class. - */ - for (MethodEntry methodEntry : methods) { - if (methodEntry.match(methodName, paramSignature, returnTypeName)) { - /* maybe the method's file entry */ - fileEntryToUse = methodEntry.getFileEntry(); - break; - } - } - if (fileEntryToUse == null) { - /* Last chance is the class's file entry. */ - fileEntryToUse = this.fileEntry; - } + /* Last chance is the class's file entry. */ + fileEntryToUse = this.fileEntry; } return new Range(symbolName, stringTable, method, fileEntryToUse, lo, hi, primaryLine); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index 8e7da1ff2150..e06b29e22562 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -239,8 +239,6 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { String className = TypeEntry.canonicalize(debugCodeInfo.ownerType()); String methodName = debugCodeInfo.name(); String symbolName = debugCodeInfo.symbolNameForMethod(); - String paramSignature = debugCodeInfo.paramSignature(); - String returnTypeName = TypeEntry.canonicalize(debugCodeInfo.valueType()); int lo = debugCodeInfo.addressLo(); int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); @@ -248,7 +246,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { /* Search for a method defining this primary range. */ ClassEntry classEntry = ensureClassEntry(className); MethodEntry methodEntry = classEntry.ensureMethodEntry(debugCodeInfo, this, debugContext); - Range primaryRange = classEntry.makePrimaryRange(methodName, symbolName, paramSignature, returnTypeName, stringTable, methodEntry, lo, hi, primaryLine); + Range primaryRange = classEntry.makePrimaryRange(symbolName, stringTable, methodEntry, lo, hi, primaryLine); debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", className, methodName, filePath, fileName, primaryLine, lo, hi); classEntry.indexPrimary(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index a3d6e1c0db28..dd82d22565b1 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -65,7 +65,7 @@ public Range(String symbolName, StringTable stringTable, MethodEntry methodEntry */ private Range(String symbolName, StringTable stringTable, MethodEntry methodEntry, FileEntry fileEntry, int lo, int hi, int line, Range primary) { - this.fileEntry = fileEntry; // TODO remove and use fileEntry from MethodEntry + this.fileEntry = fileEntry; if (fileEntry != null) { stringTable.uniqueDebugString(fileEntry.getFileName()); stringTable.uniqueDebugString(fileEntry.getPathName()); From 94957c48c44dbd9e7cbb24d173d51c629d47d921 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Thu, 8 Apr 2021 15:26:11 +0300 Subject: [PATCH 120/290] Enhance MethodEntries creation and verification Following Andrew Dinn's recommendation in https://github.com/oracle/graal/pull/3304#issuecomment-815926794, this patch creates the necessary `MethodEntry`s for each `ClassEntry` using the methodInfoProvider, It then sorts the methods list to ensure faster lookups in the phase of linking `Range`s to `MethodEntry`s. It also adds assertions to ensure that the methods list is sorted and that there are no duplicate entries in it. --- .../objectfile/debugentry/ClassEntry.java | 36 +++++++++++----- .../objectfile/debugentry/DebugInfoBase.java | 6 +-- .../objectfile/debugentry/MethodEntry.java | 43 ++++++++++++++----- 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 2bb7369844ff..3b57505b6860 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -35,9 +35,11 @@ import org.graalvm.compiler.debug.DebugContext; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.Map; /** @@ -134,7 +136,9 @@ public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInf /* Add details of fields and field types */ debugInstanceTypeInfo.fieldInfoProvider().forEach(debugFieldInfo -> this.processField(debugFieldInfo, debugInfoBase, debugContext)); /* Add details of methods and method types */ - debugInstanceTypeInfo.methodInfoProvider().forEach(methodFieldInfo -> this.processMethod(methodFieldInfo, debugInfoBase, debugContext)); + debugInstanceTypeInfo.methodInfoProvider().forEach(methodFieldInfo -> this.methods.add(this.processMethod(methodFieldInfo, debugInfoBase, debugContext))); + /* Sort methods to improve lookup speed */ + this.methods.sort(MethodEntry::compareTo); } public void indexPrimary(Range primary, List frameSizeInfos, int frameSize) { @@ -294,9 +298,7 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa * substitution */ FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); - final MethodEntry methodEntry = new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers, debugMethodInfo.isDeoptTarget()); - methods.add(methodEntry); - return methodEntry; + return new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers, debugMethodInfo.isDeoptTarget()); } @Override @@ -346,16 +348,30 @@ public Range makePrimaryRange(String symbolName, StringTable stringTable, Method return new Range(symbolName, stringTable, method, fileEntryToUse, lo, hi, primaryLine); } - public MethodEntry ensureMethodEntry(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + public MethodEntry getMethodEntry(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + assert listIsSorted(methods); String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); String paramSignature = debugMethodInfo.paramSignature(); String returnTypeName = debugMethodInfo.valueType(); - // TODO improve data structure to avoid loops... - for (MethodEntry methodEntry : methods) { - if (methodEntry.match(methodName, paramSignature, returnTypeName)) { + ListIterator methodIterator = methods.listIterator(); + while (methodIterator.hasNext()) { + MethodEntry methodEntry = methodIterator.next(); + int comparisonResult = methodEntry.compareTo(methodName, paramSignature, returnTypeName); + if (comparisonResult == 0) { return methodEntry; + } else if (comparisonResult > 0) { + methodIterator.previous(); + break; } } - return processMethod(debugMethodInfo, debugInfoBase, debugContext); + MethodEntry newMethodEntry = processMethod(debugMethodInfo, debugInfoBase, debugContext); + methodIterator.add(newMethodEntry); + return newMethodEntry; + } + + private boolean listIsSorted(List list) { + List copy = new ArrayList<>(list); + copy.sort(MethodEntry::compareTo); + return list.equals(copy); } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index e06b29e22562..8eb7c3e5bec0 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -245,7 +245,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { /* Search for a method defining this primary range. */ ClassEntry classEntry = ensureClassEntry(className); - MethodEntry methodEntry = classEntry.ensureMethodEntry(debugCodeInfo, this, debugContext); + MethodEntry methodEntry = classEntry.getMethodEntry(debugCodeInfo, this, debugContext); Range primaryRange = classEntry.makePrimaryRange(symbolName, stringTable, methodEntry, lo, hi, primaryLine); debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", className, methodName, filePath, fileName, primaryLine, lo, hi); classEntry.indexPrimary(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); @@ -263,7 +263,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { * symbol for them and don't see a break in the address range. */ ClassEntry subClassEntry = ensureClassEntry(classNameAtLine); - MethodEntry subMethodEntry = subClassEntry.ensureMethodEntry(debugLineInfo, this, debugContext); + MethodEntry subMethodEntry = subClassEntry.getMethodEntry(debugLineInfo, this, debugContext); Range subRange = new Range(symbolNameAtLine, stringTable, subMethodEntry, loAtLine, hiAtLine, line, primaryRange); classEntry.indexSubRange(subRange); try (DebugContext.Scope s = debugContext.scope("Subranges")) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java index 5b0fcb1092a2..dbf9c2aa9bc4 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,7 @@ package com.oracle.objectfile.debugentry; -public class MethodEntry extends MemberEntry { +public class MethodEntry extends MemberEntry implements Comparable { final TypeEntry[] paramTypes; final String[] paramNames; final boolean isDeoptTarget; @@ -74,30 +74,51 @@ public String getParamName(int idx) { return paramNames[idx]; } - public boolean match(String methodName, String paramSignature, String returnTypeName) { - if (!methodName.equals(this.memberName)) { - return false; + public int compareTo(String methodName, String paramSignature, String returnTypeName) { + if (!memberName.equals(methodName)) { + return memberName.compareTo(methodName); } - if (!returnTypeName.equals(valueType.getTypeName())) { - return false; + if (!valueType.getTypeName().equals(returnTypeName)) { + return valueType.getTypeName().compareTo(returnTypeName); } int paramCount = getParamCount(); if (paramCount == 0) { - return paramSignature.trim().length() == 0; + return 0 - paramSignature.trim().length(); } String[] paramTypeNames = paramSignature.split((",")); if (paramCount != paramTypeNames.length) { - return false; + return paramCount - paramTypeNames.length; } for (int i = 0; i < paramCount; i++) { if (!paramTypeNames[i].trim().equals(getParamTypeName(i))) { - return false; + return getParamTypeName(i).compareTo(paramTypeNames[i].trim()); } } - return true; + return 0; } public boolean isDeoptTarget() { return isDeoptTarget; } + + @Override + public int compareTo(MethodEntry other) { + assert other != null; + int result = methodName().compareTo(other.methodName()); + if (result == 0) { + result = valueType.getTypeName().compareTo(other.valueType.getTypeName()); + } + if (result == 0) { + result = getParamCount() - other.getParamCount(); + } + if (result == 0) { + for (int i = 0; i < getParamCount(); i++) { + result = getParamTypeName(i).compareTo(other.getParamTypeName(i)); + if (result != 0) { + break; + } + } + } + return result; + } } From 606f225c0f49237241050cb813569adb27a2d826 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 10 May 2021 11:44:24 +0300 Subject: [PATCH 121/290] Refactor: MethodEntry.compareTo --- .../objectfile/debugentry/MethodEntry.java | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java index dbf9c2aa9bc4..cca95588a76e 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java @@ -75,23 +75,29 @@ public String getParamName(int idx) { } public int compareTo(String methodName, String paramSignature, String returnTypeName) { - if (!memberName.equals(methodName)) { - return memberName.compareTo(methodName); + int nameComparison = memberName.compareTo(methodName); + if (nameComparison != 0) { + return nameComparison; } - if (!valueType.getTypeName().equals(returnTypeName)) { - return valueType.getTypeName().compareTo(returnTypeName); - } - int paramCount = getParamCount(); - if (paramCount == 0) { - return 0 - paramSignature.trim().length(); + int typeComparison = valueType.getTypeName().compareTo(returnTypeName); + if (typeComparison != 0) { + return typeComparison; } String[] paramTypeNames = paramSignature.split((",")); - if (paramCount != paramTypeNames.length) { - return paramCount - paramTypeNames.length; + int length; + if (paramSignature.trim().length() == 0) { + length = 0; + } else { + length = paramTypeNames.length; + } + int paramCountComparison = getParamCount() - length; + if (paramCountComparison != 0) { + return paramCountComparison; } - for (int i = 0; i < paramCount; i++) { - if (!paramTypeNames[i].trim().equals(getParamTypeName(i))) { - return getParamTypeName(i).compareTo(paramTypeNames[i].trim()); + for (int i = 0; i < getParamCount(); i++) { + int paraComparison = getParamTypeName(i).compareTo(paramTypeNames[i].trim()); + if (paraComparison != 0) { + return paraComparison; } } return 0; @@ -104,21 +110,24 @@ public boolean isDeoptTarget() { @Override public int compareTo(MethodEntry other) { assert other != null; - int result = methodName().compareTo(other.methodName()); - if (result == 0) { - result = valueType.getTypeName().compareTo(other.valueType.getTypeName()); + int nameComparison = methodName().compareTo(other.methodName()); + if (nameComparison != 0) { + return nameComparison; } - if (result == 0) { - result = getParamCount() - other.getParamCount(); + int typeComparison = valueType.getTypeName().compareTo(other.valueType.getTypeName()); + if (typeComparison != 0) { + return typeComparison; } - if (result == 0) { - for (int i = 0; i < getParamCount(); i++) { - result = getParamTypeName(i).compareTo(other.getParamTypeName(i)); - if (result != 0) { - break; - } + int paramCountComparison = getParamCount() - other.getParamCount(); + if (paramCountComparison != 0) { + return paramCountComparison; + } + for (int i = 0; i < getParamCount(); i++) { + int paramComparison = getParamTypeName(i).compareTo(other.getParamTypeName(i)); + if (paramComparison != 0) { + return paramComparison; } } - return result; + return 0; } } From 37e0d9d7b419913fcd6e39d12219106bcb523c50 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 10 May 2021 11:56:44 +0300 Subject: [PATCH 122/290] Optimize NativeImageDebugInfoProvider.paramTypes and paramNames --- .../image/NativeImageDebugInfoProvider.java | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 20e31c2bd8e8..d2ec18bfa2c4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -30,6 +30,7 @@ import java.lang.reflect.Modifier; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -694,9 +695,10 @@ public String paramSignature() { @Override public List paramTypes() { - LinkedList paramTypes = new LinkedList<>(); Signature signature = hostedMethod.getSignature(); - for (int i = 0; i < signature.getParameterCount(false); i++) { + int parameterCount = signature.getParameterCount(false); + List paramTypes = new ArrayList<>(parameterCount); + for (int i = 0; i < parameterCount; i++) { paramTypes.add(signature.getParameterType(i, null).toJavaName()); } return paramTypes; @@ -705,9 +707,10 @@ public List paramTypes() { @Override public List paramNames() { /* Can only provide blank names for now. */ - LinkedList paramNames = new LinkedList<>(); Signature signature = hostedMethod.getSignature(); - for (int i = 0; i < signature.getParameterCount(false); i++) { + int parameterCount = signature.getParameterCount(false); + List paramNames = new ArrayList<>(parameterCount); + for (int i = 0; i < parameterCount; i++) { paramNames.add(""); } return paramNames; @@ -986,10 +989,11 @@ public boolean isDeoptTarget() { @Override public List paramTypes() { - LinkedList paramTypes = new LinkedList<>(); Signature signature = hostedMethod.getSignature(); - for (int i = 0; i < signature.getParameterCount(false); i++) { - final JavaType parameterType = signature.getParameterType(i, null); + int parameterCount = signature.getParameterCount(false); + List paramTypes = new ArrayList<>(parameterCount); + for (int i = 0; i < parameterCount; i++) { + JavaType parameterType = signature.getParameterType(i, null); paramTypes.add(toJavaName(parameterType)); } return paramTypes; @@ -998,9 +1002,10 @@ public List paramTypes() { @Override public List paramNames() { /* Can only provide blank names for now. */ - LinkedList paramNames = new LinkedList<>(); Signature signature = hostedMethod.getSignature(); - for (int i = 0; i < signature.getParameterCount(false); i++) { + int parameterCount = signature.getParameterCount(false); + List paramNames = new ArrayList<>(parameterCount); + for (int i = 0; i < parameterCount; i++) { paramNames.add(""); } return paramNames; @@ -1147,10 +1152,11 @@ public int line() { @Override public List paramTypes() { - LinkedList paramTypes = new LinkedList<>(); Signature signature = method.getSignature(); - for (int i = 0; i < signature.getParameterCount(false); i++) { - final JavaType parameterType = signature.getParameterType(i, null); + int parameterCount = signature.getParameterCount(false); + List paramTypes = new ArrayList<>(parameterCount); + for (int i = 0; i < parameterCount; i++) { + JavaType parameterType = signature.getParameterType(i, null); paramTypes.add(toJavaName(parameterType)); } return paramTypes; @@ -1159,9 +1165,10 @@ public List paramTypes() { @Override public List paramNames() { /* Can only provide blank names for now. */ - LinkedList paramNames = new LinkedList<>(); Signature signature = method.getSignature(); - for (int i = 0; i < signature.getParameterCount(false); i++) { + int parameterCount = signature.getParameterCount(false); + List paramNames = new ArrayList<>(parameterCount); + for (int i = 0; i < parameterCount; i++) { paramNames.add(""); } return paramNames; From e9e0d4804f900ab6996e110c38e53f73af428b63 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 10 May 2021 12:11:20 +0300 Subject: [PATCH 123/290] Refactor: Use HostedMethod.isDeoptTarget() --- .../svm/hosted/image/NativeImageDebugInfoProvider.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index d2ec18bfa2c4..2c75d3df49ec 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -723,7 +723,7 @@ public String symbolNameForMethod() { @Override public boolean isDeoptTarget() { - return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); + return hostedMethod.isDeoptTarget(); } @Override @@ -984,7 +984,7 @@ public List getFrameSizeChanges() { @Override public boolean isDeoptTarget() { - return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); + return hostedMethod.isDeoptTarget(); } @Override @@ -1128,6 +1128,9 @@ public String symbolNameForMethod() { @Override public boolean isDeoptTarget() { + if (method instanceof HostedMethod) { + return ((HostedMethod) method).isDeoptTarget(); + } return name().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); } From e85e3f6798bb6e818ed3f2c74165d68db226c4a4 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 10 May 2021 12:56:35 +0300 Subject: [PATCH 124/290] Refactor: make listIsSorted static --- .../src/com/oracle/objectfile/debugentry/ClassEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 3b57505b6860..abb8e9133a39 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -369,7 +369,7 @@ public MethodEntry getMethodEntry(DebugMethodInfo debugMethodInfo, DebugInfoBase return newMethodEntry; } - private boolean listIsSorted(List list) { + private static boolean listIsSorted(List list) { List copy = new ArrayList<>(list); copy.sort(MethodEntry::compareTo); return list.equals(copy); From 84bdfd79b1e27001aa3e88e26b430d9cd6fe8fe0 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 10 May 2021 16:09:29 +0200 Subject: [PATCH 125/290] no need for making method public --- .../src/com/oracle/truffle/espresso/impl/Klass.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index c36fb31f01eb..96e2ac142b43 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -1040,7 +1040,7 @@ int getHierarchyDepth() { @CompilationFinal(dimensions = 1) private Klass[] transitiveInterfaceCache; - public final Klass[] getTransitiveInterfacesList() { + protected final Klass[] getTransitiveInterfacesList() { Klass[] transitiveInterfaces = transitiveInterfaceCache; if (transitiveInterfaces == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); From cd1fa4a9482152410e08ac20a8839d9e65487931 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 10 May 2021 16:10:24 +0200 Subject: [PATCH 126/290] handle loader constraint recording internally in class registries --- .../src/com/oracle/truffle/espresso/impl/ClassRegistries.java | 2 +- .../src/com/oracle/truffle/espresso/impl/ClassRegistry.java | 4 +++- .../truffle/espresso/redefinition/ClassRedefinition.java | 4 +--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java index 78eab436e96d..ecb87223813a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java @@ -234,7 +234,7 @@ public void checkLoadingConstraint(Symbol type, StaticObject loader1, Stat } } - public void recordConstraint(Symbol type, Klass klass, StaticObject loader) { + void recordConstraint(Symbol type, Klass klass, StaticObject loader) { assert !Types.isArray(type); if (!Types.isPrimitive(type)) { constraints.recordConstraint(type, klass, loader); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java index 5f558a2bd93d..be157750eb99 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java @@ -374,9 +374,11 @@ private ObjectKlass loadKlassRecursively(Meta meta, Symbol type, boolean n return (ObjectKlass) klass; } - public void onClassRenamed(ObjectKlass oldKlass, Symbol newName) { + public void onClassRenamed(ObjectKlass oldKlass, Symbol newName, Symbol type) { Symbol newType = context.getTypes().fromName(newName); classes.put(newType, new ClassRegistries.RegistryEntry(oldKlass)); + // record the new loading constraint + context.getRegistries().recordConstraint(type, oldKlass, oldKlass.getDefiningClassLoader()); } public void onInnerClassRemoved(Symbol type) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index 55c138bb7e7e..418a18a5e433 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -658,11 +658,9 @@ private void doRedefineClass(ChangePacket packet, List refreshSubCl context.getRegistries().removeUnloadedKlassConstraint(loadedKlass, type); } oldKlass.patchClassName(packet.info.getName()); - classRegistry.onClassRenamed(oldKlass, packet.info.getName()); + classRegistry.onClassRenamed(oldKlass, packet.info.getName(), type); InterpreterToVM.setFieldObject(StaticObject.NULL, oldKlass.mirror(), context.getMeta().java_lang_Class_name); - - context.getRegistries().recordConstraint(type, oldKlass, oldKlass.getDefiningClassLoader()); } oldKlass.redefineClass(packet, refreshSubClasses, ids); if (redefineListener.rerunClinit(oldKlass, packet.detectedChange.clinitChanged())) { From f41d2de4baa422e804268bb05a0c673419f22304 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 10 May 2021 16:11:22 +0200 Subject: [PATCH 127/290] improve remove method hooks and always throw exception when hook was not removed --- .../oracle/truffle/espresso/impl/Method.java | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) 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 ccd0f92d92ea..4ce45aa8d2ea 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 @@ -1026,15 +1026,22 @@ public synchronized void addMethodHook(MethodHook info) { hooks[hooks.length - 1] = info; } + private void expectActiveHooks() { + if (hooks.length == 0) { + throw new RuntimeException("Method: " + getNameAsString() + " expected to contain method hook"); + } + } + public synchronized void removeActiveHook(int requestId) { + expectActiveHooks(); + boolean removed = false; // shrink the array to avoid null values - if (hooks.length == 0) { - throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); - } else if (hooks.length == 1) { + if (hooks.length == 1) { // make sure it's the right hook if (hooks[0].getRequestId() == requestId) { hooks = new MethodHook[0]; hasActiveHook.set(false); + removed = true; } } else { int removeIndex = -1; @@ -1044,26 +1051,30 @@ public synchronized void removeActiveHook(int requestId) { break; } } - if (removeIndex == -1) { - throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); - } - MethodHook[] temp = new MethodHook[hooks.length - 1]; - for (int i = 0; i < temp.length; i++) { - temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; + 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; } - hooks = temp; + } + if (!removed) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); } } public synchronized void removeActiveHook(MethodHook hook) { + expectActiveHooks(); + boolean removed = false; // shrink the array to avoid null values - if (hooks.length == 0) { - throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); - } else if (hooks.length == 1) { + if (hooks.length == 1) { // make sure it's the right hook if (hooks[0] == hook) { hooks = new MethodHook[0]; hasActiveHook.set(false); + removed = true; } } else { int removeIndex = -1; @@ -1073,11 +1084,17 @@ public synchronized void removeActiveHook(MethodHook hook) { break; } } - MethodHook[] temp = new MethodHook[hooks.length - 1]; - for (int i = 0; i < temp.length; i++) { - temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; + 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; } - hooks = temp; + } + if (!removed) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); } } From cc581aa82d6e88608861ef5c7b7132a52e5d8330 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Mon, 10 May 2021 16:37:21 +0200 Subject: [PATCH 128/290] Use synchronized blocks where appropriate. Add comments about concurrency for internal redefinition plugins. Simplify class load actions handling. --- .../impl/RedefinitionPluginHandler.java | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java index 9ec7ae60c288..bc1df92394f1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -23,7 +23,6 @@ package com.oracle.truffle.espresso.redefinition.plugins.impl; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -45,8 +44,11 @@ public final class RedefinitionPluginHandler implements RedefineListener, DefineKlassListener { private final EspressoContext context; - private final Set internalPlugins = Collections.synchronizedSet(new HashSet<>(1)); - private final Map, List> classLoadActions = Collections.synchronizedMap(new HashMap<>()); + + // internal plugins are immediately activated during context + // initialization, so no need for synchronization on this set + private final Set internalPlugins = new HashSet<>(1); + private final Map, List> classLoadActions = new HashMap<>(); // The guest language HotSwap plugin handler passed // onto us if guest plugins are present at runtime. @@ -58,13 +60,15 @@ private RedefinitionPluginHandler(EspressoContext espressoContext) { @TruffleBoundary public void registerClassLoadAction(String className, ClassLoadAction action) { - Symbol type = context.getTypes().fromClassGetName(className); - List list = classLoadActions.get(type); - if (list == null) { - list = Collections.synchronizedList(new ArrayList<>()); - classLoadActions.put(type, list); + synchronized (classLoadActions) { + Symbol type = context.getTypes().fromClassGetName(className); + List list = classLoadActions.get(type); + if (list == null) { + list = new ArrayList<>(); + classLoadActions.put(type, list); + } + list.add(action); } - list.add(action); } public void registerExternalHotSwapHandler(StaticObject handler) { @@ -96,22 +100,22 @@ private void activatePlugin(InternalRedefinitionPlugin plugin) { @TruffleBoundary @Override public void onKlassDefined(ObjectKlass klass) { - // internal plugins - Symbol type = klass.getType(); - // fire registered load actions - List loadActions = classLoadActions.getOrDefault(type, Collections.emptyList()); - Iterator it = loadActions.iterator(); - while (it.hasNext()) { - ClassLoadAction loadAction = it.next(); - loadAction.fire(klass); - it.remove(); - } - if (loadActions.isEmpty()) { - classLoadActions.remove(type); + synchronized (classLoadActions) { + Symbol type = klass.getType(); + List loadActions = classLoadActions.get(type); + if (loadActions != null) { + // fire all registered load actions + Iterator it = loadActions.iterator(); + while (it.hasNext()) { + ClassLoadAction loadAction = it.next(); + loadAction.fire(klass); + } + // free up memory after firing all actions + classLoadActions.remove(type); + } } } - // listener methods @Override public boolean rerunClinit(ObjectKlass klass, boolean changed) { boolean rerun = false; From a69abb9d64bedcda690de7fc23bc90e2a8357dc5 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 10 May 2021 17:00:02 +0200 Subject: [PATCH 129/290] CI: Don't run on markdown changes --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e0931ee3c6c4..8af98848babc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,10 +5,12 @@ on: paths-ignore: - '.travis.yml' - '.github/workflows/quarkus.yml' + - '**.md' pull_request: paths-ignore: - '.travis.yml' - '.github/workflows/quarkus.yml' + - '**.md' workflow_dispatch: [] env: From 766ee0db335c346b970ae0443b965cb114fefcd6 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Mon, 10 May 2021 18:19:47 +0200 Subject: [PATCH 130/290] Fix typo. --- .../org/graalvm/compiler/nodes/spi/ArrayLengthProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/ArrayLengthProvider.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/ArrayLengthProvider.java index e432cc070004..73487a3d474b 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/ArrayLengthProvider.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/ArrayLengthProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021, 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 @@ -44,7 +44,7 @@ enum FindLengthMode { * * Values that are defined inside a loop and flow out the loop need to be proxied by * {@link ValueProxyNode}. When this mode is used, new necessary proxy nodes are created - * base on the proxies that were found while traversing the path to the length node. In + * based on the proxies that were found while traversing the path to the length node. In * addition, new {@link ValuePhiNode phi nodes} can be created. The caller is responsible * for adding these nodes to the graph, i.e., the return value can be a node that is not yet * added to the graph. From dfc5727edc31bfa6149e37ff8bf4a575fd706249 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 11 May 2021 00:27:51 +0000 Subject: [PATCH 131/290] [GR-30679] Update graal to get the traversing compilation queue PullRequest: truffleruby/2640 --- vm/mx.vm/suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index 3b9f0585d295..3d906280ce0f 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -57,7 +57,7 @@ }, { "name": "truffleruby", - "version": "f2e97df99606ad382b4ba8f1272984ecfacb95f9", + "version": "f313eb02ee0b93390cb1a7d92e2db415702d4bb1", "dynamic": True, "urls": [ {"url": "https://github.com/oracle/truffleruby.git", "kind": "git"}, From 9fc1169de9e2e67430ac44f4b63f86b9fb94156b Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 11 May 2021 09:20:38 +0200 Subject: [PATCH 132/290] method renames and a few minor refactorings and logging improvements --- .../espresso/hotswap/HotSwapHandler.java | 2 +- .../redefinition/ClassRedefinition.java | 6 ++--- .../api/InternalRedefinitionPlugin.java | 4 +-- .../plugins/impl/ExternalPluginHandler.java | 26 ++++++++++--------- .../plugins/impl/RedefineListener.java | 6 ++--- .../impl/RedefinitionPluginHandler.java | 12 ++++----- .../jdkproxy/JDKProxyRedefinitionPlugin.java | 4 +-- 7 files changed, 31 insertions(+), 29 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java index 76fb54cce033..46c6cdfec864 100644 --- a/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java +++ b/espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/HotSwapHandler.java @@ -122,7 +122,7 @@ public void postHotSwap(Class[] changedClasses) { } @SuppressWarnings("unused") - public boolean rerunClassInit(Class klass, boolean changed) { + public boolean shouldRerunClassInitializer(Class klass, boolean changed) { if (staticInitializerHotSwap.containsKey(klass)) { boolean onlyOnChange = staticInitializerHotSwap.get(klass); boolean rerun = !onlyOnChange || changed; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index 418a18a5e433..bfa1ad2a0e57 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -147,11 +147,11 @@ public static void end() { } public void addExtraReloadClasses(List redefineInfos, List additional) { - redefineListener.addExtraReloadClasses(redefineInfos, additional); + redefineListener.collectExtraClassesToReload(redefineInfos, additional); } public void runPostRedefintionListeners(ObjectKlass[] changedKlasses) { - redefineListener.postRedefition(changedKlasses); + redefineListener.postRedefinition(changedKlasses); } private static class RedefineAssumption { @@ -663,7 +663,7 @@ private void doRedefineClass(ChangePacket packet, List refreshSubCl InterpreterToVM.setFieldObject(StaticObject.NULL, oldKlass.mirror(), context.getMeta().java_lang_Class_name); } oldKlass.redefineClass(packet, refreshSubClasses, ids); - if (redefineListener.rerunClinit(oldKlass, packet.detectedChange.clinitChanged())) { + if (redefineListener.shouldRerunClassInitializer(oldKlass, packet.detectedChange.clinitChanged())) { context.getJdwpContext().rerunclinit(oldKlass); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java index e0f4191f8f7b..efa54441678c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/api/InternalRedefinitionPlugin.java @@ -43,11 +43,11 @@ public void activate(EspressoContext espressoContext, RedefinitionPluginHandler this.redefinitionPluginHandler = handler; } - public boolean reRunClinit(@SuppressWarnings("unused") ObjectKlass klass, @SuppressWarnings("unused") boolean changed) { + public boolean shouldRerunClassInitializer(@SuppressWarnings("unused") ObjectKlass klass, @SuppressWarnings("unused") boolean changed) { return false; } - public void fillExtraReloadClasses(@SuppressWarnings("unused") List redefineInfos, @SuppressWarnings("unused") List additional) { + public void collectExtraClassesToReload(@SuppressWarnings("unused") List redefineInfos, @SuppressWarnings("unused") List additional) { // default does nothing } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java index 8e810758cbcf..e7a481e79234 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java @@ -28,37 +28,39 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.jdwp.impl.JDWP; import com.oracle.truffle.espresso.runtime.StaticObject; final class ExternalPluginHandler { - private static InteropLibrary INTEROP; - private static final String RERUN_CLINIT = "rerunClassInit"; + private final InteropLibrary interopLibrary; + private static final String RERUN_CLINIT = "shouldRerunClassInitializer"; private static final String POST_HOTSWAP = "postHotSwap"; private final StaticObject guestHandler; - private ExternalPluginHandler(StaticObject handler) { + private ExternalPluginHandler(StaticObject handler, InteropLibrary library) { this.guestHandler = handler; + this.interopLibrary = library; } public static ExternalPluginHandler create(StaticObject guestHandler) throws IllegalArgumentException { - INTEROP = InteropLibrary.getFactory().create(guestHandler); + InteropLibrary library = InteropLibrary.getFactory().create(guestHandler); - boolean invocable = INTEROP.isMemberInvocable(guestHandler, RERUN_CLINIT) && - INTEROP.isMemberInvocable(guestHandler, POST_HOTSWAP); + boolean invocable = library.isMemberInvocable(guestHandler, RERUN_CLINIT) && + library.isMemberInvocable(guestHandler, POST_HOTSWAP); if (!invocable) { throw new IllegalArgumentException("guest handler does not implement expected API"); } - return new ExternalPluginHandler(guestHandler); + return new ExternalPluginHandler(guestHandler, library); } - public boolean rerunClassInit(Klass klass, boolean changed) { + public boolean shouldRerunClassInitializer(Klass klass, boolean changed) { try { - return (boolean) INTEROP.invokeMember(guestHandler, RERUN_CLINIT, klass.mirror(), changed); + return (boolean) interopLibrary.invokeMember(guestHandler, RERUN_CLINIT, klass.mirror(), changed); } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException | ArityException e) { - // TODO - log failure to invoke clinit rerun method on external plugin handler + JDWP.LOGGER.throwing(ExternalPluginHandler.class.getName(), "shouldRerunClassInitializer", e); } return false; } @@ -70,9 +72,9 @@ public void postHotSwap(Klass[] changedKlasses) { guestClasses[i] = changedKlasses[i].mirror(); } StaticObject array = StaticObject.createArray(changedKlasses[0].getMeta().java_lang_Class_array, guestClasses); - INTEROP.invokeMember(guestHandler, POST_HOTSWAP, array); + interopLibrary.invokeMember(guestHandler, POST_HOTSWAP, array); } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException | ArityException e) { - // TODO - log failure to invoke clinit rerun method on external plugin handler + JDWP.LOGGER.throwing(ExternalPluginHandler.class.getName(), "postHotSwap", e); } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java index 74f533381718..ee032a43799a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefineListener.java @@ -28,9 +28,9 @@ import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; public interface RedefineListener { - boolean rerunClinit(ObjectKlass klass, boolean changed); + boolean shouldRerunClassInitializer(ObjectKlass klass, boolean changed); - void postRedefition(ObjectKlass[] changedKlasses); + void postRedefinition(ObjectKlass[] changedKlasses); - void addExtraReloadClasses(List redefineInfos, List additional); + void collectExtraClassesToReload(List redefineInfos, List additional); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java index bc1df92394f1..fc6522ef6111 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/RedefinitionPluginHandler.java @@ -117,24 +117,24 @@ public void onKlassDefined(ObjectKlass klass) { } @Override - public boolean rerunClinit(ObjectKlass klass, boolean changed) { + public boolean shouldRerunClassInitializer(ObjectKlass klass, boolean changed) { boolean rerun = false; // internal plugins for (InternalRedefinitionPlugin plugin : internalPlugins) { - if (plugin.reRunClinit(klass, changed)) { + if (plugin.shouldRerunClassInitializer(klass, changed)) { rerun = true; break; } } // external plugins if (externalPluginHandler != null) { - rerun |= externalPluginHandler.rerunClassInit(klass, changed); + rerun |= externalPluginHandler.shouldRerunClassInitializer(klass, changed); } return rerun; } @Override - public void postRedefition(ObjectKlass[] changedKlasses) { + public void postRedefinition(ObjectKlass[] changedKlasses) { // internal plugins for (InternalRedefinitionPlugin plugin : internalPlugins) { try { @@ -151,10 +151,10 @@ public void postRedefition(ObjectKlass[] changedKlasses) { } @Override - public void addExtraReloadClasses(List redefineInfos, List additional) { + public void collectExtraClassesToReload(List redefineInfos, List additional) { // internal plugins for (InternalRedefinitionPlugin plugin : internalPlugins) { - plugin.fillExtraReloadClasses(redefineInfos, additional); + plugin.collectExtraClassesToReload(redefineInfos, additional); } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java index 24562329bff0..b10346727d42 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkproxy/JDKProxyRedefinitionPlugin.java @@ -81,7 +81,7 @@ private void addCacheEntry(ProxyCache proxyCache, KlassRef proxyInterface) { @Override @TruffleBoundary - public synchronized void fillExtraReloadClasses(List redefineInfos, List additional) { + public synchronized void collectExtraClassesToReload(List redefineInfos, List additional) { for (RedefineInfo redefineInfo : redefineInfos) { KlassRef klass = redefineInfo.getKlass(); if (klass != null) { @@ -96,7 +96,7 @@ public synchronized void fillExtraReloadClasses(List redefineInfos } @Override - public boolean reRunClinit(ObjectKlass klass, boolean changed) { + public boolean shouldRerunClassInitializer(ObjectKlass klass, boolean changed) { // changed Dynamic Proxy classes have cached Method references // in static fields, so always re-run the static initializer return changed && getContext().getMeta().java_lang_reflect_Proxy.isAssignable(klass); From bc6227feeefa3a75d3b43e92b822f596f5fc4df1 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 11 May 2021 09:22:16 +0200 Subject: [PATCH 133/290] properly handle concurrency in JDK cache plugin and clean up thread group contexts that was GC'ed --- .../jdkcaches/JDKCacheRedefinitionPlugin.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java index 66c8cbafb595..ca417e34b0e5 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/jdkcaches/JDKCacheRedefinitionPlugin.java @@ -25,7 +25,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collections; +import java.util.Iterator; import java.util.List; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -38,7 +38,7 @@ public final class JDKCacheRedefinitionPlugin extends InternalRedefinitionPlugin { - private List> threadGroupContexts = Collections.synchronizedList(new ArrayList<>(4)); + private final List> threadGroupContexts = new ArrayList<>(4); private Method flushFromCachesMethod; private Method removeBeanInfoMethod; @@ -51,21 +51,30 @@ public void activate(EspressoContext espressoContext, RedefinitionPluginHandler @Override public void postClassRedefinition(ObjectKlass[] changedKlasses) { - for (ObjectKlass changedKlass : changedKlasses) { - if (flushFromCachesMethod != null) { - flushFromCachesMethod.invokeDirect(null, changedKlass.mirror()); - } - for (WeakReference ref : threadGroupContexts) { - StaticObject context = ref.get(); - if (context != null) { - removeBeanInfoMethod.invokeDirect(context, changedKlass.mirror()); + synchronized (threadGroupContexts) { + for (ObjectKlass changedKlass : changedKlasses) { + if (flushFromCachesMethod != null) { + flushFromCachesMethod.invokeDirect(null, changedKlass.mirror()); + } + Iterator> iterator = threadGroupContexts.iterator(); + while (iterator.hasNext()) { + WeakReference ref = iterator.next(); + StaticObject context = ref.get(); + if (context != null) { + removeBeanInfoMethod.invokeDirect(context, changedKlass.mirror()); + } else { + // clean up list when context was reclaimed + iterator.remove(); + } } } } } @TruffleBoundary - public synchronized void registerThreadGroupContext(StaticObject context) { - threadGroupContexts.add(new WeakReference<>(context)); + public void registerThreadGroupContext(StaticObject context) { + synchronized (threadGroupContexts) { + threadGroupContexts.add(new WeakReference<>(context)); + } } } From d457bc2f4f2eb7a0d35905ac5ef8e23c019aa967 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 11 May 2021 09:23:17 +0200 Subject: [PATCH 134/290] only inject hotswap.jar when jdwp options is set, log when injection does not take place --- .../espresso/runtime/EspressoProperties.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java index aed3ac5642ea..c2572953e3bf 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -32,6 +32,7 @@ import java.util.List; import java.util.Objects; +import com.oracle.truffle.espresso.jdwp.impl.JDWP; import org.graalvm.home.HomeFinder; import org.graalvm.options.OptionValues; @@ -272,14 +273,20 @@ static Builder processOptions(Builder builder, OptionValues options) { bootClasspath = new ArrayList<>(options.get(EspressoOptions.BootClasspath)); } - // Inject hotswap.jar. Path espressoHome = HomeFinder.getInstance().getLanguageHomes().get(EspressoLanguage.ID); - Path hotswapJar = espressoHome.resolve("lib").resolve("hotswap.jar"); - if (Files.isReadable(hotswapJar)) { - TruffleLogger.getLogger(EspressoLanguage.ID).fine("Adding HotSwap API to the boot classpath: " + hotswapJar); - bootClasspath.add(hotswapJar); + + // Inject hotswap.jar. + // Espresso HotSwap plugin support is currently only available in debugging mode + if (EspressoOptions.JDWPOptions != null) { + Path hotswapJar = espressoHome.resolve("lib").resolve("hotswap.jar"); + if (Files.isReadable(hotswapJar)) { + TruffleLogger.getLogger(EspressoLanguage.ID).fine("Adding HotSwap API to the boot classpath: " + hotswapJar); + bootClasspath.add(hotswapJar); + } else { + TruffleLogger.getLogger(EspressoLanguage.ID).warning("hotswap.jar (HotSwap API) not found at " + espressoHome.resolve("lib")); + } } else { - TruffleLogger.getLogger(EspressoLanguage.ID).warning("hotswap.jar (HotSwap API) not found at " + espressoHome.resolve("lib")); + JDWP.LOGGER.fine(() -> "Espresso HotSwap Plugin support is disabled. HotSwap is only supported in debug mode."); } // Inject polyglot.jar. From 0ea21026b4507d2d29908093b548a9e47361347b Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 11 May 2021 09:55:54 +0300 Subject: [PATCH 135/290] Refactor: Use ArrayList instead of LinkedList --- .../objectfile/debugentry/ClassEntry.java | 24 ++++++++++--------- .../objectfile/debugentry/DebugInfoBase.java | 15 ++++++------ .../debugentry/InterfaceClassEntry.java | 4 ++-- .../objectfile/debugentry/PrimaryEntry.java | 4 ++-- .../debugentry/StructureTypeEntry.java | 4 ++-- .../elf/dwarf/DwarfARangesSectionImpl.java | 10 ++++---- .../elf/dwarf/DwarfInfoSectionImpl.java | 18 +++++++------- 7 files changed, 41 insertions(+), 38 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index abb8e9133a39..0e49c67a21be 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -53,7 +53,7 @@ public class ClassEntry extends StructureTypeEntry { /** * Details of this class's interfaces. */ - protected LinkedList interfaces; + protected List interfaces; /** * Details of the associated file. */ @@ -66,7 +66,7 @@ public class ClassEntry extends StructureTypeEntry { * A list recording details of all primary ranges included in this class sorted by ascending * address range. */ - private LinkedList primaryEntries; + private List primaryEntries; /** * An index identifying primary ranges which have already been encountered. */ @@ -78,7 +78,7 @@ public class ClassEntry extends StructureTypeEntry { /** * A list of the same files. */ - private LinkedList localFiles; + private List localFiles; /** * An index of all primary and secondary dirs referenced from this class's compilation unit. */ @@ -86,7 +86,7 @@ public class ClassEntry extends StructureTypeEntry { /** * A list of the same dirs. */ - private LinkedList localDirs; + private List localDirs; /** * This flag is true iff the entry includes methods that are deopt targets. */ @@ -94,14 +94,16 @@ public class ClassEntry extends StructureTypeEntry { public ClassEntry(String className, FileEntry fileEntry, int size) { super(className, size); - this.interfaces = new LinkedList<>(); + this.interfaces = new ArrayList<>(); this.fileEntry = fileEntry; + // methods is a sorted list and we want to be able to add more elements to it while keeping it sorted, + // so a LinkedList seems more appropriate than an ArrayList. (see getMethodEntry) this.methods = new LinkedList<>(); - this.primaryEntries = new LinkedList<>(); + this.primaryEntries = new ArrayList<>(); this.primaryIndex = new HashMap<>(); - this.localFiles = new LinkedList<>(); + this.localFiles = new ArrayList<>(); this.localFilesIndex = new HashMap<>(); - this.localDirs = new LinkedList<>(); + this.localDirs = new ArrayList<>(); this.localDirsIndex = new HashMap<>(); if (fileEntry != null) { localFiles.add(fileEntry); @@ -231,7 +233,7 @@ public FileEntry getFileEntry() { return fileEntry; } - public LinkedList getPrimaryEntries() { + public List getPrimaryEntries() { return primaryEntries; } @@ -240,11 +242,11 @@ public Object primaryIndexFor(Range primaryRange) { return primaryIndex.get(primaryRange); } - public LinkedList getLocalDirs() { + public List getLocalDirs() { return localDirs; } - public LinkedList getLocalFiles() { + public List getLocalFiles() { return localFiles; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index 8eb7c3e5bec0..b15cd4e1860d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -29,8 +29,9 @@ import java.nio.ByteOrder; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; +import java.util.List; import java.util.Map; import org.graalvm.compiler.debug.DebugContext; @@ -89,7 +90,7 @@ public abstract class DebugInfoBase { /** * List of class entries detailing class info for primary ranges. */ - private LinkedList types = new LinkedList<>(); + private List types = new ArrayList<>(); /** * index of already seen classes. */ @@ -97,7 +98,7 @@ public abstract class DebugInfoBase { /** * List of class entries detailing class info for primary ranges. */ - private LinkedList primaryClasses = new LinkedList<>(); + private List primaryClasses = new ArrayList<>(); /** * index of already seen classes. */ @@ -109,7 +110,7 @@ public abstract class DebugInfoBase { /** * List of of files which contain primary or secondary ranges. */ - private LinkedList files = new LinkedList<>(); + private List files = new ArrayList<>(); /** * Flag set to true if heap references are stored as addresses relative to a heap base register * otherwise false. @@ -426,16 +427,16 @@ public ByteOrder getByteOrder() { return byteOrder; } - public LinkedList getTypes() { + public List getTypes() { return types; } - public LinkedList getPrimaryClasses() { + public List getPrimaryClasses() { return primaryClasses; } @SuppressWarnings("unused") - public LinkedList getFiles() { + public List getFiles() { return files; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java index 9b929ca6332d..9318a068b235 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java @@ -31,7 +31,7 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; import org.graalvm.compiler.debug.DebugContext; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; @@ -40,7 +40,7 @@ public class InterfaceClassEntry extends ClassEntry { public InterfaceClassEntry(String className, FileEntry fileEntry, int size) { super(className, fileEntry, size); - implementors = new LinkedList<>(); + implementors = new ArrayList<>(); } @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java index 53aaa5a2b7d6..c61e3428d140 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java @@ -28,7 +28,7 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; /** @@ -59,7 +59,7 @@ public class PrimaryEntry { public PrimaryEntry(Range primary, List frameSizeInfos, int frameSize, ClassEntry classEntry) { this.primary = primary; this.classEntry = classEntry; - this.subranges = new LinkedList<>(); + this.subranges = new ArrayList<>(); this.frameSizeInfos = frameSizeInfos; this.frameSize = frameSize; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java index dcc80a0eb71f..d678b09029a8 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java @@ -31,7 +31,7 @@ import java.lang.reflect.Modifier; import java.nio.file.Path; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; @@ -47,7 +47,7 @@ public abstract class StructureTypeEntry extends TypeEntry { public StructureTypeEntry(String typeName, int size) { super(typeName, size); - this.fields = new LinkedList<>(); + this.fields = new ArrayList<>(); } public Stream fields() { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java index 02fad181804b..e24cda20f423 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java @@ -34,7 +34,7 @@ import com.oracle.objectfile.debugentry.Range; import org.graalvm.compiler.debug.DebugContext; -import java.util.LinkedList; +import java.util.List; import java.util.Map; /** @@ -102,7 +102,7 @@ public void createContent() { * Align to 2 * address size. */ pos += DW_AR_HEADER_PAD_SIZE; - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + List classPrimaryEntries = classEntry.getPrimaryEntries(); if (classEntry.includesDeoptTarget()) { /* Deopt targets are in a higher address range so delay emit for them. */ for (PrimaryEntry classPrimaryEntry : classPrimaryEntries) { @@ -123,7 +123,7 @@ public void createContent() { * Align to 2 * address size. */ pos += DW_AR_HEADER_PAD_SIZE; - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + List classPrimaryEntries = classEntry.getPrimaryEntries(); for (PrimaryEntry classPrimaryEntry : classPrimaryEntries) { if (classPrimaryEntry.getPrimary().isDeoptTarget()) { pos += 2 * 8; @@ -167,7 +167,7 @@ public void writeContent(DebugContext context) { int lastpos = pos; int length = DW_AR_HEADER_SIZE + DW_AR_HEADER_PAD_SIZE - 4; int cuIndex = getCUIndex(classEntry); - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + List classPrimaryEntries = classEntry.getPrimaryEntries(); /* * Count only real methods, omitting deopt targets. */ @@ -218,7 +218,7 @@ public void writeContent(DebugContext context) { int lastpos = pos; int length = DW_AR_HEADER_SIZE + DW_AR_HEADER_PAD_SIZE - 4; int cuIndex = getDeoptCUIndex(classEntry); - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + List classPrimaryEntries = classEntry.getPrimaryEntries(); /* * Count only linkage stubs. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 39ac113011f7..00e88b8626d7 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -27,7 +27,7 @@ package com.oracle.objectfile.elf.dwarf; import java.lang.reflect.Modifier; -import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Set; @@ -439,7 +439,7 @@ private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, b String compilationDirectory = classEntry.getCachePath(); log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); pos = writeAttrStrp(compilationDirectory, buffer, pos); - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + List classPrimaryEntries = classEntry.getPrimaryEntries(); /* * Specify hi and lo for the compile unit which means we also need to ensure methods within * it are listed in ascending address order. @@ -654,7 +654,7 @@ private int writeField(DebugContext context, StructureTypeEntry entry, FieldEntr private int writeMethodDeclarations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + List classPrimaryEntries = classEntry.getPrimaryEntries(); for (PrimaryEntry primaryEntry : classPrimaryEntries) { Range range = primaryEntry.getPrimary(); /* @@ -913,7 +913,7 @@ private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfa private int writeMethodLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + List classPrimaryEntries = classEntry.getPrimaryEntries(); for (PrimaryEntry primaryEntry : classPrimaryEntries) { Range range = primaryEntry.getPrimary(); if (!range.isDeoptTarget()) { @@ -1181,7 +1181,7 @@ private int writeArrayTypes(DebugContext context, ArrayTypeEntry arrayTypeEntry, private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; assert classEntry.includesDeoptTarget(); - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + List classPrimaryEntries = classEntry.getPrimaryEntries(); String fileName = classEntry.getFileName(); int lineIndex = getLineIndex(classEntry); int abbrevCode = (fileName.length() > 0 ? DwarfDebugInfo.DW_ABBREV_CODE_class_unit1 : DwarfDebugInfo.DW_ABBREV_CODE_class_unit2); @@ -1266,10 +1266,10 @@ private int writeCUHeader(byte[] buffer, int p) { } } - private static int findLo(LinkedList classPrimaryEntries, boolean isDeoptTargetCU) { + private static int findLo(List classPrimaryEntries, boolean isDeoptTargetCU) { if (!isDeoptTargetCU) { /* First entry is the one we want. */ - return classPrimaryEntries.getFirst().getPrimary().getLo(); + return classPrimaryEntries.get(0).getPrimary().getLo(); } else { /* Need the first entry which is a deopt target. */ for (PrimaryEntry primaryEntry : classPrimaryEntries) { @@ -1284,10 +1284,10 @@ private static int findLo(LinkedList classPrimaryEntries, boolean return 0; } - private static int findHi(LinkedList classPrimaryEntries, boolean includesDeoptTarget, boolean isDeoptTargetCU) { + private static int findHi(List classPrimaryEntries, boolean includesDeoptTarget, boolean isDeoptTargetCU) { if (isDeoptTargetCU || !includesDeoptTarget) { /* Either way the last entry is the one we want. */ - return classPrimaryEntries.getLast().getPrimary().getHi(); + return classPrimaryEntries.get(classPrimaryEntries.size() - 1).getPrimary().getHi(); } else { /* Need the last entry which is not a deopt target. */ int hi = 0; From 4fe62b8ba321db0a5ab1a71a45fda97747f3891d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 11 May 2021 10:30:37 +0200 Subject: [PATCH 136/290] Style fix (eclipseformat) --- .../objectfile/debugentry/ClassEntry.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 0e49c67a21be..83322f45cc37 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -26,14 +26,6 @@ package com.oracle.objectfile.debugentry; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFieldInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugInstanceTypeInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; -import org.graalvm.compiler.debug.DebugContext; - import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -42,6 +34,15 @@ import java.util.ListIterator; import java.util.Map; +import org.graalvm.compiler.debug.DebugContext; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFieldInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugInstanceTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; + /** * Track debug info associated with a Java class. */ @@ -96,7 +97,8 @@ public ClassEntry(String className, FileEntry fileEntry, int size) { super(className, size); this.interfaces = new ArrayList<>(); this.fileEntry = fileEntry; - // methods is a sorted list and we want to be able to add more elements to it while keeping it sorted, + // methods is a sorted list and we want to be able to add more elements to it while keeping + // it sorted, // so a LinkedList seems more appropriate than an ArrayList. (see getMethodEntry) this.methods = new LinkedList<>(); this.primaryEntries = new ArrayList<>(); From 7b2f61fbd4811c3c880dcd51987de9c81876fa35 Mon Sep 17 00:00:00 2001 From: Roland Schatz Date: Mon, 10 May 2021 15:54:30 +0200 Subject: [PATCH 137/290] Update URL in comment to new llvm-project github repo. --- .../src/com/oracle/truffle/llvm/parser/metadata/MDType.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sulong/projects/com.oracle.truffle.llvm.parser/src/com/oracle/truffle/llvm/parser/metadata/MDType.java b/sulong/projects/com.oracle.truffle.llvm.parser/src/com/oracle/truffle/llvm/parser/metadata/MDType.java index 9ca92e01613f..912f5a0fd45b 100644 --- a/sulong/projects/com.oracle.truffle.llvm.parser/src/com/oracle/truffle/llvm/parser/metadata/MDType.java +++ b/sulong/projects/com.oracle.truffle.llvm.parser/src/com/oracle/truffle/llvm/parser/metadata/MDType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. + * Copyright (c) 2016, 2021, Oracle and/or its affiliates. * * All rights reserved. * @@ -91,7 +91,7 @@ public void replace(MDBaseNode oldValue, MDBaseNode newValue) { } } - // @see: https://github.com/llvm-mirror/llvm/blob/master/include/llvm/BinaryFormat/Dwarf.def + // @see: https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/BinaryFormat/Dwarf.def public enum DwarfTag { UNKNOWN(-1), From fb1f66225b8e4bf372fa2659fe7ddfe6d3977513 Mon Sep 17 00:00:00 2001 From: Roland Schatz Date: Mon, 10 May 2021 16:19:17 +0200 Subject: [PATCH 138/290] Avoid insensitive wording in test code. --- .../llvm/tests/llirtestgen/LLIRTestGen.java | 20 +++++++++---------- .../truffle/llvm/tests/BaseSuiteHarness.java | 12 +++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sulong/tests/com.oracle.truffle.llvm.tests.llirtestgen/src/com/oracle/truffle/llvm/tests/llirtestgen/LLIRTestGen.java b/sulong/tests/com.oracle.truffle.llvm.tests.llirtestgen/src/com/oracle/truffle/llvm/tests/llirtestgen/LLIRTestGen.java index 377dc2d196ab..d5c40c3f1ef6 100644 --- a/sulong/tests/com.oracle.truffle.llvm.tests.llirtestgen/src/com/oracle/truffle/llvm/tests/llirtestgen/LLIRTestGen.java +++ b/sulong/tests/com.oracle.truffle.llvm.tests.llirtestgen/src/com/oracle/truffle/llvm/tests/llirtestgen/LLIRTestGen.java @@ -636,17 +636,17 @@ Optional generate(boolean debug, String progName, IDCounter idCou } private static void genFile(boolean debug, String progName, String prelude, String filename, Type type, Generator gen, O op, - boolean includeStores, boolean printFilename, Set filenameBlacklist) throws FileNotFoundException { + boolean includeStores, boolean printFilename, Set filenameIgnorelist) throws FileNotFoundException { StringBuilder str = new StringBuilder(); str.append(String.format("; Generated by %s.\n", progName)); str.append(prelude); String finalFilename = filename; - if (filenameBlacklist.contains(filename)) { + if (filenameIgnorelist.contains(filename)) { finalFilename += ".ignore"; if (debug) { - System.err.printf(progName + ": Appending .ignore to %s (%s) because it is blacklisted\n", filename, finalFilename); + System.err.printf(progName + ": Appending .ignore to %s (%s) because it is ignored\n", filename, finalFilename); } } @@ -677,7 +677,7 @@ private static String makeFilename(String base, String op, String type) { return new File(base, String.format("%s_%s.ll", op, type)).toString(); } - private static String makeBlacklistFilename(String base, String name) { + private static String makeIgnorelistFilename(String base, String name) { return new File(base, String.format("%s.ll", name)).toString(); } @@ -725,7 +725,7 @@ public static void main(String[] args) throws IOException { String progName = LLIRTestGen.class.getCanonicalName(); - Set filenameBlacklist = new HashSet<>(Arrays.asList("add_16xi1", "add_1xi1", "add_2xi1", "add_32xi1", "add_3xi1", "add_4xi1", "add_5xi1", "add_6xi1", "add_7xi1", "add_8xi1", "add_i1", + Set filenameIgnorelist = new HashSet<>(Arrays.asList("add_16xi1", "add_1xi1", "add_2xi1", "add_32xi1", "add_3xi1", "add_4xi1", "add_5xi1", "add_6xi1", "add_7xi1", "add_8xi1", "add_i1", "and_16xi1", "and_1xi1", "and_2xi1", "and_32xi1", "and_3xi1", "and_4xi1", "and_5xi1", "and_6xi1", "and_7xi1", "and_8xi1", "and_i1", "ashr_16xi1", "ashr_16xi16", "ashr_16xi8", "ashr_1xi1", "ashr_2xi1", "ashr_2xi16", "ashr_2xi32", "ashr_2xi64", "ashr_2xi8", "ashr_32xi1", "ashr_32xi8", "ashr_3xi1", "ashr_3xi16", "ashr_3xi32", "ashr_3xi32", "ashr_3xi64", "ashr_4xi1", "ashr_4xi16", "ashr_4xi32", "ashr_4xi64", "ashr_5xi1", "ashr_5xi16", "ashr_5xi32", "ashr_5xi8", "ashr_6xi1", "ashr_6xi16", "ashr_6xi32", "ashr_6xi8", @@ -767,29 +767,29 @@ public static void main(String[] args) throws IOException { "ashr_3xi8" // Works with LLVM9 but not 10, depends on undefined behavior )); if (Platform.isAArch64()) { - filenameBlacklist.addAll(Arrays.asList( + filenameIgnorelist.addAll(Arrays.asList( "ashr_3xi8", "ashr_4xi8", "bitcast_x86_fp80", "lshr_2xi8", "lshr_3xi8", "lshr_4xi8", "shl_2xi8", "shl_3xi8", "shl_4xi8", "add_4xi64", "and_4xi64", "or_4xi64", "xor_4xi64")); } - filenameBlacklist = filenameBlacklist.stream().map(s -> makeBlacklistFilename(outputDir, s)).collect(Collectors.toSet()); + filenameIgnorelist = filenameIgnorelist.stream().map(s -> makeIgnorelistFilename(outputDir, s)).collect(Collectors.toSet()); for (Type type : allTypes) { String filename; if (separateStores) { filename = makeFilename(outputDir, "store", type.toName()); - genFile(debug, progName, prelude, filename, type, LLIRTestGen::genCustom, "store", false, printFilenames, filenameBlacklist); + genFile(debug, progName, prelude, filename, type, LLIRTestGen::genCustom, "store", false, printFilenames, filenameIgnorelist); } for (UnaryOp op : UnaryOp.values()) { filename = makeFilename(outputDir, op.toString(), type.toName()); - genFile(debug, progName, prelude, filename, type, LLIRTestGen::genUnary, op, !separateStores, printFilenames, filenameBlacklist); + genFile(debug, progName, prelude, filename, type, LLIRTestGen::genUnary, op, !separateStores, printFilenames, filenameIgnorelist); } for (BinaryOp op : BinaryOp.values()) { filename = makeFilename(outputDir, op.toString(), type.toName()); - genFile(debug, progName, prelude, filename, type, LLIRTestGen::genBinary, op, !separateStores, printFilenames, filenameBlacklist); + genFile(debug, progName, prelude, filename, type, LLIRTestGen::genBinary, op, !separateStores, printFilenames, filenameIgnorelist); } } } diff --git a/sulong/tests/com.oracle.truffle.llvm.tests/src/com/oracle/truffle/llvm/tests/BaseSuiteHarness.java b/sulong/tests/com.oracle.truffle.llvm.tests/src/com/oracle/truffle/llvm/tests/BaseSuiteHarness.java index c5d109d74db2..3758c4e4e93a 100644 --- a/sulong/tests/com.oracle.truffle.llvm.tests/src/com/oracle/truffle/llvm/tests/BaseSuiteHarness.java +++ b/sulong/tests/com.oracle.truffle.llvm.tests/src/com/oracle/truffle/llvm/tests/BaseSuiteHarness.java @@ -299,8 +299,8 @@ public static void reportDiscoveryReport() { private static final int PERCENT = 100; protected static void printStatistics(String name, Path source, Path config, Predicate filter) { - Set whiteList = getListEntries(source, config, CommonTestUtils.isIncludeFile); - Set blackList = getListEntries(source, config, CommonTestUtils.isExcludeFile); + Set includeList = getListEntries(source, config, CommonTestUtils.isIncludeFile); + Set excludeList = getListEntries(source, config, CommonTestUtils.isExcludeFile); Set files = CommonTestUtils.getFiles(source); Map statisticTotalFiles = CommonTestUtils.supportedFiles.stream().collect(Collectors.toMap(s -> s, s -> 0)); Map statisticTotalNoExcludeFiles = CommonTestUtils.supportedFiles.stream().collect(Collectors.toMap(s -> s, s -> 0)); @@ -316,9 +316,9 @@ protected static void printStatistics(String name, Path source, Path config, Pre } } - // count available test files minus blackList + // count available test files minus excludeList for (Path f : files) { - if (filter.test(f) && !blackList.contains(f)) { + if (filter.test(f) && !excludeList.contains(f)) { String fileEnding = CommonTestUtils.getFileEnding(f.toString()); if (CommonTestUtils.supportedFiles.contains(fileEnding)) { statisticTotalNoExcludeFiles.put(fileEnding, statisticTotalNoExcludeFiles.get(fileEnding) + 1); @@ -327,7 +327,7 @@ protected static void printStatistics(String name, Path source, Path config, Pre } // count running test files - for (Path f : whiteList) { + for (Path f : includeList) { if (filter.test(f)) { String fileEnding = CommonTestUtils.getFileEnding(f.toString()); if (CommonTestUtils.supportedFiles.contains(fileEnding)) { @@ -378,7 +378,7 @@ private static Set getListEntries(Path suiteDirectory, Path configDir, Pre } return results; } catch (IOException e) { - throw new AssertionError("Error creating whitelist.", e); + throw new AssertionError("Error creating test filter list.", e); } } } From 541e9c8fed250aa37b5e9b23e3b4c738e436760a Mon Sep 17 00:00:00 2001 From: Roland Schatz Date: Mon, 10 May 2021 17:44:54 +0200 Subject: [PATCH 139/290] Remove external image from README.md. --- sulong/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sulong/README.md b/sulong/README.md index f4886e89d237..01b15ef09f9a 100644 --- a/sulong/README.md +++ b/sulong/README.md @@ -1,5 +1,3 @@ -![Sulong Logo](https://raw.githubusercontent.com/mrigger/sulong-logos/master/sulong_black_with_text_transparent_300x185.png) - Sulong is a high-performance LLVM bitcode runtime built on the [GraalVM](https://www.graalvm.org) by [Oracle Labs](https://labs.oracle.com). @@ -36,8 +34,5 @@ resources: # Further Information -The logo was designed by -[Valentina Caruso](https://www.behance.net/volantina-). - Sulong is developed in a research collaboration with [Johannes Kepler University, Linz](http://www.ssw.jku.at). From e4a5a97112763de49cbf9b4137ecae3b3cfa4d63 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Tue, 11 May 2021 12:21:00 +0200 Subject: [PATCH 140/290] minor formatting fix --- .../redefinition/plugins/impl/ExternalPluginHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java index e7a481e79234..86a70eb973de 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java @@ -48,7 +48,7 @@ public static ExternalPluginHandler create(StaticObject guestHandler) throws Ill InteropLibrary library = InteropLibrary.getFactory().create(guestHandler); boolean invocable = library.isMemberInvocable(guestHandler, RERUN_CLINIT) && - library.isMemberInvocable(guestHandler, POST_HOTSWAP); + library.isMemberInvocable(guestHandler, POST_HOTSWAP); if (!invocable) { throw new IllegalArgumentException("guest handler does not implement expected API"); From ae0ee3b83f1ce2068575717a9d85cb1c5b18f5c8 Mon Sep 17 00:00:00 2001 From: Roland Schatz Date: Tue, 11 May 2021 12:34:22 +0200 Subject: [PATCH 141/290] Fix potential null return in LLVMLanguage.parse when racing with GC. --- .../truffle/llvm/runtime/LLVMLanguage.java | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java index 3da44deae962..9449c939f92c 100644 --- a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java +++ b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java @@ -496,26 +496,21 @@ public LLVMStatementNode createInitializeContextNode() { */ @Override protected CallTarget parse(ParsingRequest request) { - synchronized (libraryCacheLock) { - Source source = request.getSource(); - String path = source.getPath(); - CallTarget callTarget; - WeakReference ref; - if (source.isCached()) { - ref = libraryCache.get(path); - if (ref == null) { - callTarget = getCapability(Loader.class).load(getContext(), source, idGenerater.generateID()); - WeakReference prev = libraryCache.putIfAbsent(path, new WeakReference<>(callTarget)); - // To ensure the call target in the cache is always returned in case of - // concurrency. - if (prev != null) { - callTarget = prev.get(); - } - } else { - callTarget = ref.get(); + Source source = request.getSource(); + String path = source.getPath(); + if (source.isCached()) { + synchronized (libraryCacheLock) { + CallTarget cached = getCachedLibrary(path); + if (cached == null) { + assert !libraryCache.containsKey(path) : "racy insertion despite lock?"; + + cached = getCapability(Loader.class).load(getContext(), source, idGenerater.generateID()); + WeakReference ref = new WeakReference<>(cached); + libraryCache.put(path, ref); } - return callTarget; + return cached; } + } else { // just get the id here and give it to the parserDriver return getCapability(Loader.class).load(getContext(), source, idGenerater.generateID()); } @@ -523,10 +518,16 @@ protected CallTarget parse(ParsingRequest request) { public CallTarget getCachedLibrary(String path) { synchronized (libraryCacheLock) { - if (libraryCache.get(path) == null) { + WeakReference ref = libraryCache.get(path); + if (ref == null) { return null; } - return libraryCache.get(path).get(); + CallTarget ret = ref.get(); + if (ret == null) { + // clean up the map after an entry has been cleared by the GC + libraryCache.removeKey(path); + } + return ret; } } From 77bb21f6c57bd43f074cd5474fcc286ac720812f Mon Sep 17 00:00:00 2001 From: Roland Schatz Date: Tue, 11 May 2021 12:50:32 +0200 Subject: [PATCH 142/290] Lazy cache cleanup when weak references in LLVM code cache die. --- .../truffle/llvm/runtime/LLVMLanguage.java | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java index 9449c939f92c..d8b6885cf738 100644 --- a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java +++ b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java @@ -66,6 +66,7 @@ import com.oracle.truffle.llvm.runtime.pointer.LLVMPointer; import com.oracle.truffle.llvm.runtime.target.TargetTriple; import com.oracle.truffle.llvm.toolchain.config.LLVMConfig; +import java.lang.ref.ReferenceQueue; import org.graalvm.collections.EconomicMap; import org.graalvm.options.OptionDescriptors; import org.graalvm.options.OptionValues; @@ -135,8 +136,21 @@ private ContextExtensionKey cast(Class target @CompilationFinal private LLVMMemory cachedLLVMMemory; private final EconomicMap internalFileScopes = EconomicMap.create(); - private final EconomicMap> libraryCache = EconomicMap.create(); + + private static final class LibraryCacheEntry extends WeakReference { + + final String path; + + LibraryCacheEntry(LLVMLanguage language, String path, CallTarget callTarget) { + super(callTarget, language.libraryCacheQueue); + this.path = path; + } + } + + private final EconomicMap libraryCache = EconomicMap.create(); + private final ReferenceQueue libraryCacheQueue = new ReferenceQueue<>(); private final Object libraryCacheLock = new Object(); + private final EconomicMap librarySources = EconomicMap.create(); private final IDGenerater idGenerater = new IDGenerater(); @@ -505,8 +519,8 @@ protected CallTarget parse(ParsingRequest request) { assert !libraryCache.containsKey(path) : "racy insertion despite lock?"; cached = getCapability(Loader.class).load(getContext(), source, idGenerater.generateID()); - WeakReference ref = new WeakReference<>(cached); - libraryCache.put(path, ref); + LibraryCacheEntry entry = new LibraryCacheEntry(this, path, cached); + libraryCache.put(path, entry); } return cached; } @@ -516,16 +530,31 @@ protected CallTarget parse(ParsingRequest request) { } } + private void lazyCacheCleanup() { + /* + * Just lazily clean up one entry. We do this on every lookup. Under the assumption that + * lookups are more frequent than insertions, this will eventually catch up and remove every + * GCed entry. + */ + LibraryCacheEntry ref = (LibraryCacheEntry) libraryCacheQueue.poll(); + if (ref != null) { + libraryCache.removeKey(ref.path); + } + } + public CallTarget getCachedLibrary(String path) { synchronized (libraryCacheLock) { - WeakReference ref = libraryCache.get(path); - if (ref == null) { + lazyCacheCleanup(); + LibraryCacheEntry entry = libraryCache.get(path); + if (entry == null) { return null; } - CallTarget ret = ref.get(); + + assert entry.path.equals(path); + CallTarget ret = entry.get(); if (ret == null) { // clean up the map after an entry has been cleared by the GC - libraryCache.removeKey(path); + libraryCache.removeKey(entry.path); } return ret; } From 9bc7e127b4093be80b6a04c82a6fb76b4f5bdbde Mon Sep 17 00:00:00 2001 From: stepan Date: Tue, 11 May 2021 15:35:27 +0200 Subject: [PATCH 143/290] Move CachedDataRaceTest to the correct directory --- .../oracle/truffle/api/dsl/{ => test}/CachedDataRaceTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/{ => test}/CachedDataRaceTest.java (97%) diff --git a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedDataRaceTest.java similarity index 97% rename from truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java rename to truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedDataRaceTest.java index eb58acd35254..2a06f46e581a 100644 --- a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/CachedDataRaceTest.java +++ b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedDataRaceTest.java @@ -38,7 +38,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.oracle.truffle.api.dsl; +package com.oracle.truffle.api.dsl.test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -49,6 +49,8 @@ import org.junit.Test; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; From a607279ad1db8d155c80e6fe08046694a62f5fd0 Mon Sep 17 00:00:00 2001 From: Jakub Chaloupka Date: Mon, 10 May 2021 12:11:06 +0200 Subject: [PATCH 144/290] Exclude PolyglotLanguageContext and PolyglotContextImpl from context heap boundaries assert, copy all asserts to boundaries in case asserts are disabled. --- .../test/ContextInterruptStandaloneTest.java | 2 +- .../test/RetainedSizeComputationTest.java | 44 ++++++ ...a => RetainedSizeContextBoundaryTest.java} | 131 +++++++++++++++++- .../polyglot/ObjectSizeCalculator.java | 22 ++- 4 files changed, 195 insertions(+), 4 deletions(-) rename truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/{TestProxyObjectNotReachable.java => RetainedSizeContextBoundaryTest.java} (53%) diff --git a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/ContextInterruptStandaloneTest.java b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/ContextInterruptStandaloneTest.java index f4baf0443d3f..2668f4f6b516 100644 --- a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/ContextInterruptStandaloneTest.java +++ b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/ContextInterruptStandaloneTest.java @@ -85,7 +85,7 @@ public class ContextInterruptStandaloneTest extends AbstractPolyglotTest { @Test public void testCancelDuringHostSleep() throws ExecutionException, InterruptedException { CountDownLatch beforeSleep = new CountDownLatch(1); - setupEnv(Context.newBuilder(ProxyLanguage.ID).allowHostClassLoading(true).allowHostClassLookup((s) -> true).allowHostAccess(HostAccess.ALL), + setupEnv(Context.newBuilder(ProxyLanguage.ID).allowHostClassLookup((s) -> true).allowHostAccess(HostAccess.ALL), new ProxyLanguage() { @Override protected CallTarget parse(TruffleLanguage.ParsingRequest request) throws Exception { diff --git a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/RetainedSizeComputationTest.java b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/RetainedSizeComputationTest.java index e5938e9341b5..c529836278d8 100644 --- a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/RetainedSizeComputationTest.java +++ b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/RetainedSizeComputationTest.java @@ -56,6 +56,8 @@ import java.util.concurrent.atomic.AtomicInteger; import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Engine; +import org.graalvm.polyglot.ResourceLimits; import org.graalvm.polyglot.Source; import org.junit.Assert; import org.junit.Assume; @@ -64,7 +66,12 @@ import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleOptions; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.impl.DefaultTruffleRuntime; +import com.oracle.truffle.api.instrumentation.EventContext; +import com.oracle.truffle.api.instrumentation.ExecutionEventListener; +import com.oracle.truffle.api.instrumentation.SourceSectionFilter; +import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.instrumentation.TruffleInstrument; import com.oracle.truffle.api.test.CompileImmediatelyCheck; @@ -95,6 +102,43 @@ public void testRetainedSizeSingleThreaded() throws IOException { } } + @Test + public void testRetainedSizeWithStatementLimit() { + Assume.assumeFalse(TruffleOptions.AOT); + Assume.assumeFalse(Truffle.getRuntime() instanceof DefaultTruffleRuntime); + try (Engine engine = Engine.create()) { + Context.newBuilder().engine(engine).build().close(); + ResourceLimits resourceLimits = ResourceLimits.newBuilder().statementLimit(5, source -> true).build(); + try (Context context = Context.newBuilder().engine(engine).resourceLimits(resourceLimits).build()) { + TruffleInstrument.Env instrumentEnv = context.getEngine().getInstruments().get("InstrumentationUpdateInstrument").lookup(TruffleInstrument.Env.class); + context.initialize(InstrumentationTestLanguage.ID); + instrumentEnv.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.StatementTag.class).build(), new ExecutionEventListener() { + @Override + public void onEnter(EventContext ctx, VirtualFrame frame) { + + } + + @Override + public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) { + long retainedSize = instrumentEnv.calculateContextHeapSize(instrumentEnv.getEnteredContext(), 16L * 1024L * 1024L, new AtomicBoolean(false)); + Assert.assertTrue(retainedSize > 0L); + Assert.assertTrue(retainedSize < 16L * 1024L * 1024L); + } + + @Override + public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) { + + } + }); + /* + * StatementIncrementNode stores PolyglotContextImpl in frames. The retained size + * computation should stop on PolyglotContextImpl and don't fail. + */ + context.eval(InstrumentationTestLanguage.ID, "ROOT(STATEMENT)"); + } + } + } + @Test public void testRetainedSizeCanceledAtStart() throws IOException { try (Context context = Context.create()) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/TestProxyObjectNotReachable.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/RetainedSizeContextBoundaryTest.java similarity index 53% rename from truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/TestProxyObjectNotReachable.java rename to truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/RetainedSizeContextBoundaryTest.java index 181ffa8e5f6e..2fade77e2872 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/TestProxyObjectNotReachable.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/RetainedSizeContextBoundaryTest.java @@ -46,23 +46,33 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.HostAccess; import org.graalvm.polyglot.Value; import org.graalvm.polyglot.proxy.ProxyObject; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; +import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleOptions; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.impl.DefaultTruffleRuntime; +import com.oracle.truffle.api.instrumentation.TruffleInstrument; +import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.SourceSection; -public class TestProxyObjectNotReachable extends AbstractPolyglotTest { +public class RetainedSizeContextBoundaryTest extends AbstractPolyglotTest { @ExportLibrary(InteropLibrary.class) static class ScopeObject implements TruffleObject { final Map members = Collections.synchronizedMap(new LinkedHashMap<>()); @@ -184,8 +194,127 @@ public void putMember(String key, Value value) { } }); + /* + * Calculation should stop at PolyglotProxy, and so the ProxyObject and thus also the + * polyglot Context should not be reachable. If it were, the calculation would fail on + * assert. + */ long retainedSize = instrumentEnv.calculateContextHeapSize(instrumentEnv.getEnteredContext(), 16L * 1024L * 1024L, new AtomicBoolean(false)); Assert.assertTrue(retainedSize > 0); Assert.assertTrue(retainedSize < 16L * 1024L * 1024L); } + + @SuppressWarnings("static-method") + @ExportLibrary(InteropLibrary.class) + static class HeapSizeExecutable implements TruffleObject { + private TruffleInstrument.Env instrumentEnv; + + @ExportMessage + final boolean isExecutable() { + return true; + } + + @SuppressWarnings("unused") + @ExportMessage + @CompilerDirectives.TruffleBoundary + final Object execute(Object[] arguments) { + return instrumentEnv.calculateContextHeapSize(instrumentEnv.getEnteredContext(), 16L * 1024L * 1024L, new AtomicBoolean(false)); + } + } + + @Test + public void testRetainedSizeWithHostToGuestRootNode() { + Assume.assumeFalse(TruffleOptions.AOT); + Assume.assumeFalse(Truffle.getRuntime() instanceof DefaultTruffleRuntime); + HeapSizeExecutable heapSizeExecutable = new HeapSizeExecutable(); + setupEnv(Context.create(), new ProxyLanguage() { + private CallTarget target; + + @Override + protected CallTarget parse(ParsingRequest request) { + com.oracle.truffle.api.source.Source source = request.getSource(); + if (target == null) { + target = Truffle.getRuntime().createCallTarget(new RootNode(languageInstance) { + + @Override + public Object execute(VirtualFrame frame) { + return heapSizeExecutable; + } + + @Override + public SourceSection getSourceSection() { + return source.createSection(1); + } + + }); + } + return target; + } + }); + heapSizeExecutable.instrumentEnv = instrumentEnv; + Value val = context.eval(ProxyLanguage.ID, ""); + /* + * Value#execute() uses HostToGuestRootNode which stores PolyglotLanguageContext in a frame. + * The retained size computation should stop on PolyglotLanguageContext and don't fail. + */ + long retainedSize = val.execute().asLong(); + Assert.assertTrue(retainedSize > 0); + Assert.assertTrue(retainedSize < 16L * 1024L * 1024L); + } + + private static TruffleInstrument.Env staticInstrumentEnv; + + public static long calculateRetainedSize() { + return staticInstrumentEnv.calculateContextHeapSize(staticInstrumentEnv.getEnteredContext(), 16L * 1024L * 1024L, new AtomicBoolean(false)); + } + + @Test + public void testRetainedSizeWithGuestToHostRootNode() { + Assume.assumeFalse(TruffleOptions.AOT); + Assume.assumeFalse(Truffle.getRuntime() instanceof DefaultTruffleRuntime); + setupEnv(Context.newBuilder().allowHostClassLookup((s) -> true).allowHostAccess(HostAccess.ALL), new ProxyLanguage() { + private CallTarget target; + + @Override + protected CallTarget parse(ParsingRequest request) { + com.oracle.truffle.api.source.Source source = request.getSource(); + if (target == null) { + target = Truffle.getRuntime().createCallTarget(new RootNode(languageInstance) { + + @Override + public Object execute(VirtualFrame frame) { + TruffleLanguage.ContextReference languageContext = lookupContextReference(ProxyLanguage.class); + Object thisTestClass = languageContext.get().getEnv().lookupHostSymbol(RetainedSizeContextBoundaryTest.class.getName()); + try { + return InteropLibrary.getUncached().invokeMember(thisTestClass, "calculateRetainedSize"); + } catch (UnsupportedMessageException | ArityException | UnknownIdentifierException | UnsupportedTypeException e) { + throw new AssertionError(e); + } + } + + @Override + public SourceSection getSourceSection() { + return source.createSection(1); + } + + }); + } + return target; + } + }); + staticInstrumentEnv = instrumentEnv; + try { + /* + * The following uses GuestToHostRootNode which stores PolyglotLanguageContext in a + * frame. The retained size computation should stop on PolyglotLanguageContext and don't + * fail. + */ + Value val = context.eval(ProxyLanguage.ID, ""); + long retainedSize = val.asLong(); + Assert.assertTrue(retainedSize > 0); + Assert.assertTrue(retainedSize < 16L * 1024L * 1024L); + } finally { + staticInstrumentEnv = null; + } + } } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/ObjectSizeCalculator.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/ObjectSizeCalculator.java index b213e42a6dfa..e35adab4c561 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/ObjectSizeCalculator.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/ObjectSizeCalculator.java @@ -255,7 +255,7 @@ private static boolean isContextHeapBoundary(Object obj) { return true; } - assert !(obj instanceof PolyglotImpl.VMObject) && + assert (!(obj instanceof PolyglotImpl.VMObject) || obj instanceof PolyglotLanguageContext || obj instanceof PolyglotContextImpl) && !(obj instanceof PolyglotContextConfig) && !(obj instanceof TruffleLanguage.Provider) && !(obj instanceof ExecutionEventListener) && @@ -302,7 +302,25 @@ private static boolean isContextHeapBoundary(Object obj) { (obj instanceof TruffleContext) || (obj instanceof ContextLocal) || - (obj instanceof ContextThreadLocal); + (obj instanceof ContextThreadLocal) || + + /* + * For safety, copy the asserts here in case asserts are disabled. + */ + (obj instanceof PolyglotImpl.VMObject) || + (obj instanceof PolyglotContextConfig) || + (obj instanceof TruffleLanguage.Provider) || + (obj instanceof ExecutionEventListener) || + (obj instanceof ClassValue) || + (obj instanceof ClassLoader) || + (obj instanceof HostWrapper) || + (obj instanceof Value) || + (obj instanceof Context) || + (obj instanceof Engine) || + (obj instanceof Language) || + (obj instanceof Instrument) || + (obj instanceof org.graalvm.polyglot.Source) || + (obj instanceof org.graalvm.polyglot.SourceSection); } private static ClassInfo canProceed(Map, ClassInfo> classInfos, Object obj) { From c9fa093972721cfc97781222b2c24131b49d089b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Pejovi=C4=87?= Date: Tue, 11 May 2021 18:09:44 +0200 Subject: [PATCH 145/290] Adjust to lazy resolution of NinjaProject.buildDependencies --- truffle/mx.truffle/mx_truffle.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/truffle/mx.truffle/mx_truffle.py b/truffle/mx.truffle/mx_truffle.py index dc33d9ad52e9..7d80f615d9f3 100644 --- a/truffle/mx.truffle/mx_truffle.py +++ b/truffle/mx.truffle/mx_truffle.py @@ -720,10 +720,14 @@ def getArchivableResults(self, use_relpath=True, single=False): ]) ) - self.buildDependencies = self.delegate.buildDependencies self.include_dirs = self.delegate.include_dirs self.libs = self.delegate.libs + def resolveDeps(self): + super(LibffiBuilderProject, self).resolveDeps() + self.delegate.resolveDeps() + self.buildDependencies += self.delegate.buildDependencies + @property def sources(self): assert len(self.deps) == 1, '{} must depend only on its sources'.format(self.name) From 9c523b0f5fed06df5a2fef602bbd69dbe7e01b08 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Mon, 10 May 2021 18:30:18 +0200 Subject: [PATCH 146/290] Avoid unnecessary CompilerToVM call and IllegalArgumentException when canonicalizing array length read. --- .../compiler/nodes/memory/ReadNode.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java index 5115734d97fc..65db5b58aaea 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021, 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 @@ -114,7 +114,15 @@ public static ValueNode canonicalizeRead(ValueNode read, AddressNode address, Lo if (tool.canonicalizeReads() && address instanceof OffsetAddressNode) { OffsetAddressNode objAddress = (OffsetAddressNode) address; ValueNode object = objAddress.getBase(); - if (metaAccess != null && object.isConstant() && !object.isNullConstant() && objAddress.getOffset().isConstant()) { + // Note: readConstant cannot be used to read the array length, so in order to avoid an + // unnecessary CompilerToVM.readFieldValue call ending in an IllegalArgumentException, + // check if we are reading the array length location first. + if (locationIdentity.equals(ARRAY_LENGTH_LOCATION)) { + ValueNode length = GraphUtil.arrayLength(object, ArrayLengthProvider.FindLengthMode.CANONICALIZE_READ, tool.getConstantReflection()); + if (length != null) { + return length; + } + } else if (metaAccess != null && object.isConstant() && !object.isNullConstant() && objAddress.getOffset().isConstant()) { long displacement = objAddress.getOffset().asJavaConstant().asLong(); int stableDimension = ((ConstantNode) object).getStableDimension(); if (locationIdentity.isImmutable() || stableDimension > 0) { @@ -125,12 +133,6 @@ public static ValueNode canonicalizeRead(ValueNode read, AddressNode address, Lo } } } - if (locationIdentity.equals(ARRAY_LENGTH_LOCATION)) { - ValueNode length = GraphUtil.arrayLength(object, ArrayLengthProvider.FindLengthMode.CANONICALIZE_READ, tool.getConstantReflection()); - if (length != null) { - return length; - } - } if (locationIdentity instanceof CanonicalizableLocation) { CanonicalizableLocation canonicalize = (CanonicalizableLocation) locationIdentity; ValueNode result = canonicalize.canonicalizeRead(read, address, object, tool); From 768ede3be3ac3b77d79d8e2837780af62112a5ed Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Tue, 11 May 2021 13:09:57 +0200 Subject: [PATCH 147/290] [GR-31263] Make DynamicObjectLibary.putWithFlags more robust. --- .../oracle/truffle/object/LayoutStrategy.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/truffle/src/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java b/truffle/src/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java index 9136c84e4335..5803f6922af4 100644 --- a/truffle/src/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java +++ b/truffle/src/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java @@ -112,7 +112,7 @@ protected ShapeImpl defineProperty(ShapeImpl oldShape, Object key, Object value, return definePropertyGeneralize(oldShape, existing, value, locationFactory, putFlags); } } else { - return definePropertyChangeFlags(oldShape, value, propertyFlags, existing, putFlags); + return definePropertyChangeFlags(oldShape, existing, value, propertyFlags, putFlags); } } } @@ -158,32 +158,27 @@ private Location createLocationForValue(ShapeImpl oldShape, Object value, long p protected abstract Location createLocationForValue(ShapeImpl shape, Object value, long putFlags); - private ShapeImpl definePropertyChangeFlags(ShapeImpl oldShape, Object value, int propertyFlags, Property existing, long putFlags) { - assert existing != null && propertyFlags != existing.getFlags(); - Location oldLocation = existing.getLocation(); - Location newLocation; - if (oldLocation.canSet(value)) { - newLocation = oldLocation; + protected ShapeImpl definePropertyChangeFlags(ShapeImpl oldShape, Property existing, Object value, int propertyFlags, long putFlags) { + assert existing.getFlags() != propertyFlags; + oldShape.onPropertyTransition(existing); + if (existing.getLocation().canSet(value)) { + Property newProperty = Property.create(existing.getKey(), existing.getLocation(), propertyFlags); + return replaceProperty(oldShape, existing, newProperty); } else { - newLocation = oldShape.allocator().locationForValueUpcast(value, oldLocation, putFlags); + return generalizePropertyWithFlags(oldShape, existing, value, propertyFlags, putFlags); } - Property newProperty = Property.create(existing.getKey(), newLocation, propertyFlags); - oldShape.onPropertyTransition(existing); - return replaceProperty(oldShape, existing, newProperty); } - /** @since 1.0 */ protected ShapeImpl definePropertyGeneralize(ShapeImpl oldShape, Property oldProperty, Object value, LocationFactory locationFactory, long putFlags) { + oldShape.onPropertyTransition(oldProperty); if (Flags.isSeparateShape(putFlags)) { Location newLocation = createLocationForValue(oldShape, value, putFlags, locationFactory); Property newProperty = oldProperty.relocate(newLocation); - oldShape.onPropertyTransition(oldProperty); return separateReplaceProperty(oldShape, oldProperty, newProperty); } else if (oldProperty.getLocation().isValue()) { Location newLocation = createLocationForValue(oldShape, value, putFlags, locationFactory); Property newProperty = oldProperty.relocate(newLocation); // Always use direct replace for value locations to avoid shape explosion - oldShape.onPropertyTransition(oldProperty); return directReplaceProperty(oldShape, oldProperty, newProperty); } else { return generalizeProperty(oldProperty, value, oldShape, oldShape, putFlags); @@ -198,6 +193,13 @@ protected ShapeImpl generalizeProperty(Property oldProperty, Object value, Shape return replaceProperty(nextShape, oldProperty, newProperty); } + protected ShapeImpl generalizePropertyWithFlags(ShapeImpl currentShape, Property oldProperty, Object value, int propertyFlags, long putFlags) { + assert !oldProperty.getLocation().canSet(value); + Location newLocation = currentShape.allocator().locationForValueUpcast(value, oldProperty.getLocation(), putFlags); + Property newProperty = Property.create(oldProperty.getKey(), newLocation, propertyFlags); + return replaceProperty(currentShape, oldProperty, newProperty); + } + /** @since 0.17 or earlier */ protected void propertySetFallback(Property property, DynamicObject store, Object value, ShapeImpl currentShape) { ShapeImpl oldShape = currentShape; @@ -427,7 +429,8 @@ protected ShapeImpl applyTransition(ShapeImpl shape, Transition transition, bool Property property = ((AddPropertyTransition) transition).getProperty(); ShapeImpl newShape; if (append) { - newShape = shape.append(property); + Property newProperty = property.relocate(shape.allocator().moveLocation(property.getLocation())); + newShape = addProperty(shape, newProperty, true); } else { newShape = addProperty(shape, property, false); } From 3c0d87fccef532f2dbb3c5ac4af5198c522f3142 Mon Sep 17 00:00:00 2001 From: Martin Entlicher Date: Fri, 7 May 2021 16:30:15 +0200 Subject: [PATCH 148/290] Reduce distracting suspends on function exits. (GR-31240) --- .../tools/chromeinspector/InspectorDebugger.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/InspectorDebugger.java b/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/InspectorDebugger.java index 26d011bc387d..ae3d8901b604 100644 --- a/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/InspectorDebugger.java +++ b/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/InspectorDebugger.java @@ -1082,10 +1082,16 @@ public void onSuspend(SuspendedEvent se) { // Debugger has been disabled while waiting on locks return; } - if (se.hasSourceElement(SourceElement.ROOT) && !se.hasSourceElement(SourceElement.STATEMENT) && se.getSuspendAnchor() == SuspendAnchor.BEFORE && se.getBreakpoints().isEmpty()) { - // Suspend requested and we're at the begining of a ROOT. - debuggerSession.suspendNextExecution(); - return; + DebugValue returnValue = se.getReturnValue(); + if (se.hasSourceElement(SourceElement.ROOT) && se.getBreakpoints().isEmpty()) { + if ((!se.hasSourceElement(SourceElement.STATEMENT) && se.getSuspendAnchor() == SuspendAnchor.BEFORE) || + (se.getSuspendAnchor() == SuspendAnchor.AFTER && returnValue == null)) { + // We're at the begining of a `RootTag` node, or + // we're at the end of `RootTag` node and have no return value. + // Do not suspend in this case, not to cause distrations. + se.prepareStepInto(STEP_CONFIG); + return; + } } synchronized (suspendLock) { running = false; @@ -1097,7 +1103,6 @@ public void onSuspend(SuspendedEvent se) { runningUnwind = false; } JSONObject jsonParams = new JSONObject(); - DebugValue returnValue = se.getReturnValue(); if (!se.hasSourceElement(SourceElement.ROOT)) { // It is misleading to see return values on call exit, // when we show it at function exit From 3e9680a269181289df946f0e46ce778b2097b61f Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Tue, 11 May 2021 21:15:32 +0000 Subject: [PATCH 149/290] [GR-21590] Update imports PullRequest: graalpython/1791 --- vm/mx.vm/suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index 3d906280ce0f..572d48f83d01 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -75,7 +75,7 @@ }, { "name": "graalpython", - "version": "876a674a09e095da7a4c8d31a8eeb3ada5fbff8d", + "version": "9338e0373116e46820f2b0f8f212942d184ab364", "dynamic": True, "urls": [ {"url": "https://github.com/graalvm/graalpython.git", "kind": "git"}, From 93d958cac2738ccc70e729545b12f2cef8dfe267 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 12 May 2021 09:25:58 +0200 Subject: [PATCH 150/290] remove the need for a special extra reloader thread --- .../espresso/jdwp/api/JDWPContext.java | 14 +---- .../jdwp/impl/DebuggerController.java | 5 +- .../redefinition/ClassRedefinition.java | 4 ++ .../espresso/runtime/JDWPContextImpl.java | 59 +++++-------------- 4 files changed, 24 insertions(+), 58 deletions(-) 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 098d0447a79f..d7daef44a6d6 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 @@ -493,19 +493,11 @@ public interface JDWPContext { Node getInstrumentableNode(Node node); /** - * Determines if hostThread is a VM internal thread. + * Determines if the current thread is a VM internal thread. * - * @param hostThread the thread - * @return true if hostThread is a VM internal thread + * @return true if current thread is a VM internal thread */ - boolean isSystemThread(Thread hostThread); - - /** - * Sets the Truffle context. - * - * @param con the Truffle context. - */ - void setTruffleContext(TruffleContext con); + boolean isSystemThread(); /** * Returns the current BCI of the node. 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 0e078768ae4c..c94a3ff44b08 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 @@ -534,7 +534,6 @@ public void leaveTruffleContext() { @Override public void onLanguageContextInitialized(TruffleContext con, @SuppressWarnings("unused") LanguageInfo language) { truffleContext = con; - context.setTruffleContext(con); } public void suspend(CallFrame currentFrame, Object thread, byte suspendPolicy, List> jobs, SteppingInfo steppingInfo, boolean breakpointHit) { @@ -760,12 +759,12 @@ private class SuspendedCallbackImpl implements SuspendedCallback { @Override public void onSuspend(SuspendedEvent event) { - Thread hostThread = Thread.currentThread(); - if (context.isSystemThread(hostThread)) { + if (context.isSystemThread()) { // always allow VM threads to run guest code without // the risk of being suspended return; } + Thread hostThread = Thread.currentThread(); Object currentThread = getContext().asGuestThread(hostThread); JDWP.LOGGER.fine(() -> "Suspended at: " + event.getSourceSection() + " in thread: " + getThreadName(currentThread)); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index bfa1ad2a0e57..0b9b5fb5c088 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -146,6 +146,10 @@ public static void end() { } } + public static boolean isRedefineThread() { + return redefineThread == Thread.currentThread(); + } + public void addExtraReloadClasses(List redefineInfos, List additional) { redefineListener.collectExtraClassesToReload(redefineInfos, additional); } 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 698bebdf80d7..7a4526dd48d9 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 @@ -29,11 +29,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; import java.util.regex.Matcher; -import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.frame.Frame; @@ -85,15 +82,13 @@ public final class JDWPContextImpl implements JDWPContext { private static final InteropLibrary UNCACHED = InteropLibrary.getUncached(); private final EspressoContext context; - private TruffleContext truffleContext; private final Ids ids; private JDWPSetup setup; private VMListener eventListener = new EmptyListener(); private final ClassRedefinition classRedefinition; private InnerClassRedefiner innerClassRedefiner; private final RedefinitionPluginHandler redefinitionPluginHandler; - private Thread reloaderThread; - private final BlockingQueue queue = new ArrayBlockingQueue<>(128); + private final ArrayList classInitializerActions = new ArrayList<>(1); public JDWPContextImpl(EspressoContext context) { this.context = context; @@ -704,8 +699,8 @@ public Node getInstrumentableNode(Node node) { } @Override - public boolean isSystemThread(Thread hostThread) { - return hostThread == reloaderThread; + public boolean isSystemThread() { + return ClassRedefinition.isRedefineThread(); } public long getBCI(Node rawNode, Frame frame) { @@ -717,46 +712,11 @@ public long getBCI(Node rawNode, Frame frame) { return instrumentableNode.getCurrentBCI(frame); } - @Override - public void setTruffleContext(TruffleContext con) { - this.truffleContext = con; - } - - private void initializeReloaderThread() { - reloaderThread = new Thread(new Runnable() { - @Override - public void run() { - Object prev = null; - try { - prev = truffleContext.enter(null); - while (!reloaderThread.isInterrupted()) { - try { - queue.take().fire(); - } catch (InterruptedException e) { - reloaderThread.interrupt(); - } catch (Throwable t) { - // while rerunning class initializers - // in the guest, some anomalies are to - // be expected. Treat those as non-fatal - JDWP.LOGGER.fine(() -> "Exception caught while re-running "); - } - } - } finally { - truffleContext.leave(null, prev); - } - } - }, "reloader"); - reloaderThread.start(); - } - public void rerunclinit(ObjectKlass oldKlass) { - queue.add(new ReloadingAction(oldKlass)); + classInitializerActions.add(new ReloadingAction(oldKlass)); } public synchronized int redefineClasses(List redefineInfos) { - if (reloaderThread == null) { - initializeReloaderThread(); - } // list to collect all changed classes List changedKlasses = new ArrayList<>(redefineInfos.size()); try { @@ -774,6 +734,17 @@ public synchronized int redefineClasses(List redefineInfos) { classRedefinition.addExtraReloadClasses(redefineInfos, additional); // redefine additional classes now doRedefine(additional, changedKlasses); + + // re-run all registered class initializers before ending transaction + classInitializerActions.forEach((reloadingAction) -> { + try { + reloadingAction.fire(); + } catch (Throwable t) { + // Some anomalies when rerunning class initializers + // to be expected. Treat them as non-fatal. + JDWP.LOGGER.warning(() -> "exception while re-running a class initializer!"); + } + }); } catch (RedefintionNotSupportedException ex) { return ex.getErrorCode(); } finally { From 3a5c569fd93ff48b45473ec56518e8836dea3961 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 12 May 2021 09:33:23 +0200 Subject: [PATCH 151/290] unused import --- .../src/com/oracle/truffle/espresso/jdwp/api/JDWPContext.java | 1 - 1 file changed, 1 deletion(-) 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 5d318a3c705d..10146c95f677 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 @@ -25,7 +25,6 @@ import java.nio.file.Path; import java.util.List; -import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.nodes.Node; From 81bf17ec1581f982c503f15d9ce7a72eee4dfb4f Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 22 Feb 2021 16:02:08 +0100 Subject: [PATCH 152/290] Move StaticProperty to the new staticobject project. The code does not compile. Issues are fixed in the following commits. --- espresso/mx.espresso/suite.py | 12 +++++++++++- .../espresso/staticobject}/StaticProperty.java | 4 ++-- .../oracle/truffle/espresso/impl/LinkedField.java | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) rename espresso/src/{com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl => com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject}/StaticProperty.java (98%) diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index 5fefe391e000..22506444ba4b 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -85,9 +85,9 @@ "subDir": "src", "sourceDirs": ["src"], "dependencies": [ - "truffle:TRUFFLE_API", "truffle:TRUFFLE_NFI", "com.oracle.truffle.espresso.jdwp", + "com.oracle.truffle.espresso.staticobject", ], "uses": [ "com.oracle.truffle.espresso._native.NativeAccess.Provider", @@ -99,6 +99,16 @@ "checkPackagePrefix": False, # java.lang.ref.PublicFinalReference }, + "com.oracle.truffle.espresso.staticobject": { + "subDir": "src", + "sourceDirs": ["src"], + "dependencies": [ + "truffle:TRUFFLE_API", + ], + "javaCompliance": "1.8+", + "checkstyle": "com.oracle.truffle.espresso", + }, + "com.oracle.truffle.espresso.processor": { "subDir": "src", "sourceDirs": ["src"], diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java similarity index 98% rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/StaticProperty.java rename to espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index a4fff1d82057..93c8a3259231 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.espresso.impl; +package com.oracle.truffle.espresso.staticobject; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.espresso.meta.JavaKind; @@ -33,7 +33,7 @@ public class StaticProperty { private final byte internalKind; private final int offset; - StaticProperty(JavaKind kind, int offset) { + protected StaticProperty(JavaKind kind, int offset) { this.internalKind = getInternalKind(kind); this.offset = offset; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java index 89f73655213c..01abd4112e48 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java @@ -27,6 +27,7 @@ import com.oracle.truffle.espresso.descriptors.Symbol.Type; import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.runtime.Attribute; +import com.oracle.truffle.espresso.staticobject.StaticProperty; final class LinkedField extends StaticProperty { private final ParserField parserField; From 5525533e95fd14620eed1eeee897819a691630e1 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 22 Feb 2021 16:03:47 +0100 Subject: [PATCH 153/290] StaticProperty cannot depend on the UnsafeAccess class. --- .../espresso/staticobject/StaticProperty.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 93c8a3259231..5c0cab028368 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -25,11 +25,12 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.runtime.StaticObject; -import com.oracle.truffle.espresso.vm.UnsafeAccess; import sun.misc.Unsafe; +import java.lang.reflect.Field; + public class StaticProperty { - private static final Unsafe UNSAFE = UnsafeAccess.get(); + private static final Unsafe UNSAFE = getUnsafe(); private final byte internalKind; private final int offset; @@ -285,4 +286,18 @@ public final void setShortVolatile(StaticObject obj, short value) { checkKind(JavaKind.Short); UNSAFE.putShortVolatile(obj.getPrimitiveFieldStorage(), offset, value); } + + private static Unsafe getUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException e) { + } + try { + Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafeInstance.setAccessible(true); + return (Unsafe) theUnsafeInstance.get(Unsafe.class); + } catch (Exception e) { + throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); + } + } } From d07e4dda6ac6ba2ba75544716a65ec8f8030a622 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 22 Feb 2021 16:06:50 +0100 Subject: [PATCH 154/290] StaticProperty cannot depend on the JavaKind class. --- .../espresso/staticobject/StaticProperty.java | 99 ++++++------ .../staticobject/StaticPropertyKind.java | 145 ++++++++++++++++++ .../truffle/espresso/impl/LinkedField.java | 2 +- .../truffle/espresso/impl/ParserField.java | 28 ++++ 4 files changed, 223 insertions(+), 51 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticPropertyKind.java diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 5c0cab028368..4da712eec9f2 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -23,7 +23,6 @@ package com.oracle.truffle.espresso.staticobject; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.runtime.StaticObject; import sun.misc.Unsafe; @@ -34,13 +33,13 @@ public class StaticProperty { private final byte internalKind; private final int offset; - protected StaticProperty(JavaKind kind, int offset) { + protected StaticProperty(StaticPropertyKind kind, int offset) { this.internalKind = getInternalKind(kind); this.offset = offset; } - private static byte getInternalKind(JavaKind javaKind) { - return javaKind.toByte(); + private static byte getInternalKind(StaticPropertyKind kind) { + return kind.toByte(); } /** @@ -50,240 +49,240 @@ public int getOffset() { return offset; } - private void checkKind(JavaKind kind) { + private void checkKind(StaticPropertyKind kind) { if (this.internalKind != getInternalKind(kind)) { CompilerDirectives.transferToInterpreterAndInvalidate(); - String kindName = JavaKind.fromByte(internalKind).name(); + String kindName = StaticPropertyKind.valueOf(internalKind).name(); throw new RuntimeException("Static property of '" + kindName + "' kind cannot be accessed as '" + kind.name() + "'"); } } // Object field access public final Object getObject(StaticObject obj) { - checkKind(JavaKind.Object); + checkKind(StaticPropertyKind.Object); return UNSAFE.getObject(obj.getObjectFieldStorage(), (long) offset); } public final Object getObjectVolatile(StaticObject obj) { - checkKind(JavaKind.Object); + checkKind(StaticPropertyKind.Object); return UNSAFE.getObjectVolatile(obj.getObjectFieldStorage(), offset); } public final void setObject(StaticObject obj, Object value) { - checkKind(JavaKind.Object); + checkKind(StaticPropertyKind.Object); UNSAFE.putObject(obj.getObjectFieldStorage(), (long) offset, value); } public final void setObjectVolatile(StaticObject obj, Object value) { - checkKind(JavaKind.Object); + checkKind(StaticPropertyKind.Object); UNSAFE.putObjectVolatile(obj.getObjectFieldStorage(), offset, value); } public final boolean compareAndSwapObject(StaticObject obj, Object before, Object after) { - checkKind(JavaKind.Object); + checkKind(StaticPropertyKind.Object); return UNSAFE.compareAndSwapObject(obj.getObjectFieldStorage(), offset, before, after); } public final Object getAndSetObject(StaticObject obj, Object value) { - checkKind(JavaKind.Object); + checkKind(StaticPropertyKind.Object); return UNSAFE.getAndSetObject(obj.getObjectFieldStorage(), offset, value); } // boolean field access public final boolean getBoolean(StaticObject obj) { - checkKind(JavaKind.Boolean); + checkKind(StaticPropertyKind.Boolean); return UNSAFE.getBoolean(obj.getPrimitiveFieldStorage(), (long) offset); } public final boolean getBooleanVolatile(StaticObject obj) { - checkKind(JavaKind.Boolean); + checkKind(StaticPropertyKind.Boolean); return UNSAFE.getBooleanVolatile(obj.getPrimitiveFieldStorage(), offset); } public final void setBoolean(StaticObject obj, boolean value) { - checkKind(JavaKind.Boolean); + checkKind(StaticPropertyKind.Boolean); UNSAFE.putBoolean(obj.getPrimitiveFieldStorage(), (long) offset, value); } public final void setBooleanVolatile(StaticObject obj, boolean value) { - checkKind(JavaKind.Boolean); + checkKind(StaticPropertyKind.Boolean); UNSAFE.putBooleanVolatile(obj.getPrimitiveFieldStorage(), offset, value); } // byte field access public final byte getByte(StaticObject obj) { - checkKind(JavaKind.Byte); + checkKind(StaticPropertyKind.Byte); return UNSAFE.getByte(obj.getPrimitiveFieldStorage(), (long) offset); } public final byte getByteVolatile(StaticObject obj) { - checkKind(JavaKind.Byte); + checkKind(StaticPropertyKind.Byte); return UNSAFE.getByteVolatile(obj.getPrimitiveFieldStorage(), offset); } public final void setByte(StaticObject obj, byte value) { - checkKind(JavaKind.Byte); + checkKind(StaticPropertyKind.Byte); UNSAFE.putByte(obj.getPrimitiveFieldStorage(), (long) offset, value); } public final void setByteVolatile(StaticObject obj, byte value) { - checkKind(JavaKind.Byte); + checkKind(StaticPropertyKind.Byte); UNSAFE.putByteVolatile(obj.getPrimitiveFieldStorage(), offset, value); } // char field access public final char getChar(StaticObject obj) { - checkKind(JavaKind.Char); + checkKind(StaticPropertyKind.Char); return UNSAFE.getChar(obj.getPrimitiveFieldStorage(), (long) offset); } public final char getCharVolatile(StaticObject obj) { - checkKind(JavaKind.Char); + checkKind(StaticPropertyKind.Char); return UNSAFE.getCharVolatile(obj.getPrimitiveFieldStorage(), offset); } public final void setChar(StaticObject obj, char value) { - checkKind(JavaKind.Char); + checkKind(StaticPropertyKind.Char); UNSAFE.putChar(obj.getPrimitiveFieldStorage(), (long) offset, value); } public final void setCharVolatile(StaticObject obj, char value) { - checkKind(JavaKind.Char); + checkKind(StaticPropertyKind.Char); UNSAFE.putCharVolatile(obj.getPrimitiveFieldStorage(), offset, value); } // double field access public final double getDouble(StaticObject obj) { - checkKind(JavaKind.Double); + checkKind(StaticPropertyKind.Double); return UNSAFE.getDouble(obj.getPrimitiveFieldStorage(), (long) offset); } public final double getDoubleVolatile(StaticObject obj) { - checkKind(JavaKind.Double); + checkKind(StaticPropertyKind.Double); return UNSAFE.getDoubleVolatile(obj.getPrimitiveFieldStorage(), offset); } public final void setDouble(StaticObject obj, double value) { - checkKind(JavaKind.Double); + checkKind(StaticPropertyKind.Double); UNSAFE.putDouble(obj.getPrimitiveFieldStorage(), (long) offset, value); } public final void setDoubleVolatile(StaticObject obj, double value) { - checkKind(JavaKind.Double); + checkKind(StaticPropertyKind.Double); UNSAFE.putDoubleVolatile(obj.getPrimitiveFieldStorage(), offset, value); } // float field access public final float getFloat(StaticObject obj) { - checkKind(JavaKind.Float); + checkKind(StaticPropertyKind.Float); return UNSAFE.getFloat(obj.getPrimitiveFieldStorage(), (long) offset); } public final float getFloatVolatile(StaticObject obj) { - checkKind(JavaKind.Float); + checkKind(StaticPropertyKind.Float); return UNSAFE.getFloatVolatile(obj.getPrimitiveFieldStorage(), offset); } public final void setFloat(StaticObject obj, float value) { - checkKind(JavaKind.Float); + checkKind(StaticPropertyKind.Float); UNSAFE.putFloat(obj.getPrimitiveFieldStorage(), (long) offset, value); } public final void setFloatVolatile(StaticObject obj, float value) { - checkKind(JavaKind.Float); + checkKind(StaticPropertyKind.Float); UNSAFE.putFloatVolatile(obj.getPrimitiveFieldStorage(), offset, value); } // int field access public final int getInt(StaticObject obj) { - checkKind(JavaKind.Int); + checkKind(StaticPropertyKind.Int); return UNSAFE.getInt(obj.getPrimitiveFieldStorage(), (long) offset); } public final int getIntVolatile(StaticObject obj) { - checkKind(JavaKind.Int); + checkKind(StaticPropertyKind.Int); return UNSAFE.getIntVolatile(obj.getPrimitiveFieldStorage(), offset); } public final void setInt(StaticObject obj, int value) { - checkKind(JavaKind.Int); + checkKind(StaticPropertyKind.Int); UNSAFE.putInt(obj.getPrimitiveFieldStorage(), (long) offset, value); } public final void setIntVolatile(StaticObject obj, int value) { - checkKind(JavaKind.Int); + checkKind(StaticPropertyKind.Int); UNSAFE.putIntVolatile(obj.getPrimitiveFieldStorage(), offset, value); } public final boolean compareAndSwapInt(StaticObject obj, int before, int after) { - checkKind(JavaKind.Int); + checkKind(StaticPropertyKind.Int); return UNSAFE.compareAndSwapInt(obj.getPrimitiveFieldStorage(), offset, before, after); } public final int getAndAddInt(StaticObject obj, int value) { - checkKind(JavaKind.Int); + checkKind(StaticPropertyKind.Int); return UNSAFE.getAndAddInt(obj.getPrimitiveFieldStorage(), offset, value); } public final int getAndSetInt(StaticObject obj, int value) { - checkKind(JavaKind.Int); + checkKind(StaticPropertyKind.Int); return UNSAFE.getAndSetInt(obj.getPrimitiveFieldStorage(), offset, value); } // long field access public final long getLong(StaticObject obj) { - checkKind(JavaKind.Long); + checkKind(StaticPropertyKind.Long); return UNSAFE.getLong(obj.getPrimitiveFieldStorage(), (long) offset); } public final long getLongVolatile(StaticObject obj) { - checkKind(JavaKind.Long); + checkKind(StaticPropertyKind.Long); return UNSAFE.getLongVolatile(obj.getPrimitiveFieldStorage(), offset); } public final void setLong(StaticObject obj, long value) { - checkKind(JavaKind.Long); + checkKind(StaticPropertyKind.Long); UNSAFE.putLong(obj.getPrimitiveFieldStorage(), (long) offset, value); } public final void setLongVolatile(StaticObject obj, long value) { - checkKind(JavaKind.Long); + checkKind(StaticPropertyKind.Long); UNSAFE.putLongVolatile(obj.getPrimitiveFieldStorage(), offset, value); } public final boolean compareAndSwapLong(StaticObject obj, long before, long after) { - checkKind(JavaKind.Long); + checkKind(StaticPropertyKind.Long); return UNSAFE.compareAndSwapLong(obj.getPrimitiveFieldStorage(), offset, before, after); } public final long getAndAddLong(StaticObject obj, long value) { - checkKind(JavaKind.Long); + checkKind(StaticPropertyKind.Long); return UNSAFE.getAndAddLong(obj.getPrimitiveFieldStorage(), offset, value); } public final long getAndSetLong(StaticObject obj, long value) { - checkKind(JavaKind.Long); + checkKind(StaticPropertyKind.Long); return UNSAFE.getAndSetLong(obj.getPrimitiveFieldStorage(), offset, value); } // short field access public final short getShort(StaticObject obj) { - checkKind(JavaKind.Short); + checkKind(StaticPropertyKind.Short); return UNSAFE.getShort(obj.getPrimitiveFieldStorage(), (long) offset); } public final short getShortVolatile(StaticObject obj) { - checkKind(JavaKind.Short); + checkKind(StaticPropertyKind.Short); return UNSAFE.getShortVolatile(obj.getPrimitiveFieldStorage(), offset); } public final void setShort(StaticObject obj, short value) { - checkKind(JavaKind.Short); + checkKind(StaticPropertyKind.Short); UNSAFE.putShort(obj.getPrimitiveFieldStorage(), (long) offset, value); } public final void setShortVolatile(StaticObject obj, short value) { - checkKind(JavaKind.Short); + checkKind(StaticPropertyKind.Short); UNSAFE.putShortVolatile(obj.getPrimitiveFieldStorage(), offset, value); } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticPropertyKind.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticPropertyKind.java new file mode 100644 index 000000000000..820fb80813a5 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticPropertyKind.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2021, 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.staticobject; + +public enum StaticPropertyKind { + /** + * The primitive long kind. + */ + Long, // 0 + + /** + * The primitive double kind. + */ + Double, // 1 + + /** + * The primitive int kind. + */ + Int, // 2 + + /** + * The primitive float kind. + */ + Float, // 3 + + /** + * The primitive short kind. + */ + Short, // 4 + + /** + * The primitive char kind. + */ + Char, // 5 + + /** + * The primitive byte kind. + */ + Byte, // 6 + + /** + * The primitive boolean kind. + */ + Boolean, // 7 + + /** + * The Object kind. + */ + Object; // 8 + + static final int N_PRIMITIVES = 8; + static final byte BYTE_ARRAY = 9; + static final byte OBJECT_ARRAY = 10; + + static String getDescriptor(int i) { + if (i == StaticPropertyKind.Long.ordinal()) { + return "J"; + } else if (i == StaticPropertyKind.Double.ordinal()) { + return "D"; + } else if (i == StaticPropertyKind.Int.ordinal()) { + return "I"; + } else if (i == StaticPropertyKind.Float.ordinal()) { + return "F"; + } else if (i == StaticPropertyKind.Short.ordinal()) { + return "S"; + } else if (i == StaticPropertyKind.Char.ordinal()) { + return "C"; + } else if (i == StaticPropertyKind.Byte.ordinal()) { + return "B"; + } else if (i == StaticPropertyKind.Boolean.ordinal()) { + return "Z"; + } else if (i == StaticPropertyKind.Object.ordinal()) { + return "Ljava/lang/Object;"; + } else if (i == BYTE_ARRAY) { + return "[B"; + } else if (i == OBJECT_ARRAY) { + return "[Ljava/lang/Object;"; + } else { + throw new IllegalArgumentException("Invalid StaticPropertyKind: " + i); + } + } + + /** + * Number of bytes that are necessary to represent a value of this kind. + * + * @return the number of bytes + */ + static int getByteCount(int b) { + if (b == StaticPropertyKind.Boolean.ordinal()) { + return 1; + } else { + return getBitCount(b) >> 3; + } + } + + /** + * Number of bits that are necessary to represent a value of this kind. + * + * @return the number of bits + */ + private static int getBitCount(int b) { + if (b == StaticPropertyKind.Boolean.ordinal()) { + return 1; + } else if (b == StaticPropertyKind.Byte.ordinal()) { + return 8; + } else if (b == StaticPropertyKind.Char.ordinal() || b == StaticPropertyKind.Short.ordinal()) { + return 16; + } else if (b == StaticPropertyKind.Float.ordinal() || b == StaticPropertyKind.Int.ordinal()) { + return 32; + } else if (b == StaticPropertyKind.Double.ordinal() || b == StaticPropertyKind.Long.ordinal()) { + return 64; + } else { + throw new IllegalArgumentException("Invalid StaticPropertyKind: " + b); + } + } + + static StaticPropertyKind valueOf(byte b) { + return StaticPropertyKind.values()[b]; + } + + byte toByte() { + assert StaticPropertyKind.values().length < java.lang.Byte.MAX_VALUE; + return (byte) ordinal(); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java index 01abd4112e48..99ce4a616b40 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java @@ -34,7 +34,7 @@ final class LinkedField extends StaticProperty { private final int slot; LinkedField(ParserField parserField, int slot, int offset) { - super(parserField.getKind(), offset); + super(parserField.getPropertyKind(), offset); this.parserField = parserField; this.slot = slot; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserField.java index 4e86fa58f334..6b54f6b9190b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserField.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserField.java @@ -29,6 +29,7 @@ import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.runtime.Attribute; +import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import java.lang.reflect.Modifier; @@ -83,4 +84,31 @@ public boolean isStatic() { public JavaKind getKind() { return Types.getJavaKind(type); } + + public StaticPropertyKind getPropertyKind() { + if (type.length() == 1) { + char ch = (char) type.byteAt(0); + switch (ch) { + case 'Z': + return StaticPropertyKind.Boolean; + case 'C': + return StaticPropertyKind.Char; + case 'F': + return StaticPropertyKind.Float; + case 'D': + return StaticPropertyKind.Double; + case 'B': + return StaticPropertyKind.Byte; + case 'S': + return StaticPropertyKind.Short; + case 'I': + return StaticPropertyKind.Int; + case 'J': + return StaticPropertyKind.Long; + default: + throw new IllegalArgumentException("unknown primitive or void type character: " + ch); + } + } + return StaticPropertyKind.Object; + } } From 2d0c9fbd91e84561433bdd7fcf745923d9c2b2f8 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 22 Feb 2021 16:36:40 +0100 Subject: [PATCH 155/290] StaticProperty cannot depend on the StaticObject class. --- .../espresso/staticobject/StaticProperty.java | 180 +++++++++--------- .../oracle/truffle/espresso/impl/Field.java | 80 ++++---- 2 files changed, 129 insertions(+), 131 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 4da712eec9f2..fac864f1ac3f 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -23,10 +23,8 @@ package com.oracle.truffle.espresso.staticobject; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.espresso.runtime.StaticObject; -import sun.misc.Unsafe; - import java.lang.reflect.Field; +import sun.misc.Unsafe; public class StaticProperty { private static final Unsafe UNSAFE = getUnsafe(); @@ -58,232 +56,232 @@ private void checkKind(StaticPropertyKind kind) { } // Object field access - public final Object getObject(StaticObject obj) { + public final Object getObject(Object obj) { checkKind(StaticPropertyKind.Object); - return UNSAFE.getObject(obj.getObjectFieldStorage(), (long) offset); + return UNSAFE.getObject(obj, (long) offset); } - public final Object getObjectVolatile(StaticObject obj) { + public final Object getObjectVolatile(Object obj) { checkKind(StaticPropertyKind.Object); - return UNSAFE.getObjectVolatile(obj.getObjectFieldStorage(), offset); + return UNSAFE.getObjectVolatile(obj, offset); } - public final void setObject(StaticObject obj, Object value) { + public final void setObject(Object obj, Object value) { checkKind(StaticPropertyKind.Object); - UNSAFE.putObject(obj.getObjectFieldStorage(), (long) offset, value); + UNSAFE.putObject(obj, (long) offset, value); } - public final void setObjectVolatile(StaticObject obj, Object value) { + public final void setObjectVolatile(Object obj, Object value) { checkKind(StaticPropertyKind.Object); - UNSAFE.putObjectVolatile(obj.getObjectFieldStorage(), offset, value); + UNSAFE.putObjectVolatile(obj, offset, value); } - public final boolean compareAndSwapObject(StaticObject obj, Object before, Object after) { + public final boolean compareAndSwapObject(Object obj, Object before, Object after) { checkKind(StaticPropertyKind.Object); - return UNSAFE.compareAndSwapObject(obj.getObjectFieldStorage(), offset, before, after); + return UNSAFE.compareAndSwapObject(obj, offset, before, after); } - public final Object getAndSetObject(StaticObject obj, Object value) { + public final Object getAndSetObject(Object obj, Object value) { checkKind(StaticPropertyKind.Object); - return UNSAFE.getAndSetObject(obj.getObjectFieldStorage(), offset, value); + return UNSAFE.getAndSetObject(obj, offset, value); } // boolean field access - public final boolean getBoolean(StaticObject obj) { + public final boolean getBoolean(Object obj) { checkKind(StaticPropertyKind.Boolean); - return UNSAFE.getBoolean(obj.getPrimitiveFieldStorage(), (long) offset); + return UNSAFE.getBoolean(obj, (long) offset); } - public final boolean getBooleanVolatile(StaticObject obj) { + public final boolean getBooleanVolatile(Object obj) { checkKind(StaticPropertyKind.Boolean); - return UNSAFE.getBooleanVolatile(obj.getPrimitiveFieldStorage(), offset); + return UNSAFE.getBooleanVolatile(obj, offset); } - public final void setBoolean(StaticObject obj, boolean value) { + public final void setBoolean(Object obj, boolean value) { checkKind(StaticPropertyKind.Boolean); - UNSAFE.putBoolean(obj.getPrimitiveFieldStorage(), (long) offset, value); + UNSAFE.putBoolean(obj, (long) offset, value); } - public final void setBooleanVolatile(StaticObject obj, boolean value) { + public final void setBooleanVolatile(Object obj, boolean value) { checkKind(StaticPropertyKind.Boolean); - UNSAFE.putBooleanVolatile(obj.getPrimitiveFieldStorage(), offset, value); + UNSAFE.putBooleanVolatile(obj, offset, value); } // byte field access - public final byte getByte(StaticObject obj) { + public final byte getByte(Object obj) { checkKind(StaticPropertyKind.Byte); - return UNSAFE.getByte(obj.getPrimitiveFieldStorage(), (long) offset); + return UNSAFE.getByte(obj, (long) offset); } - public final byte getByteVolatile(StaticObject obj) { + public final byte getByteVolatile(Object obj) { checkKind(StaticPropertyKind.Byte); - return UNSAFE.getByteVolatile(obj.getPrimitiveFieldStorage(), offset); + return UNSAFE.getByteVolatile(obj, offset); } - public final void setByte(StaticObject obj, byte value) { + public final void setByte(Object obj, byte value) { checkKind(StaticPropertyKind.Byte); - UNSAFE.putByte(obj.getPrimitiveFieldStorage(), (long) offset, value); + UNSAFE.putByte(obj, (long) offset, value); } - public final void setByteVolatile(StaticObject obj, byte value) { + public final void setByteVolatile(Object obj, byte value) { checkKind(StaticPropertyKind.Byte); - UNSAFE.putByteVolatile(obj.getPrimitiveFieldStorage(), offset, value); + UNSAFE.putByteVolatile(obj, offset, value); } // char field access - public final char getChar(StaticObject obj) { + public final char getChar(Object obj) { checkKind(StaticPropertyKind.Char); - return UNSAFE.getChar(obj.getPrimitiveFieldStorage(), (long) offset); + return UNSAFE.getChar(obj, (long) offset); } - public final char getCharVolatile(StaticObject obj) { + public final char getCharVolatile(Object obj) { checkKind(StaticPropertyKind.Char); - return UNSAFE.getCharVolatile(obj.getPrimitiveFieldStorage(), offset); + return UNSAFE.getCharVolatile(obj, offset); } - public final void setChar(StaticObject obj, char value) { + public final void setChar(Object obj, char value) { checkKind(StaticPropertyKind.Char); - UNSAFE.putChar(obj.getPrimitiveFieldStorage(), (long) offset, value); + UNSAFE.putChar(obj, (long) offset, value); } - public final void setCharVolatile(StaticObject obj, char value) { + public final void setCharVolatile(Object obj, char value) { checkKind(StaticPropertyKind.Char); - UNSAFE.putCharVolatile(obj.getPrimitiveFieldStorage(), offset, value); + UNSAFE.putCharVolatile(obj, offset, value); } // double field access - public final double getDouble(StaticObject obj) { + public final double getDouble(Object obj) { checkKind(StaticPropertyKind.Double); - return UNSAFE.getDouble(obj.getPrimitiveFieldStorage(), (long) offset); + return UNSAFE.getDouble(obj, (long) offset); } - public final double getDoubleVolatile(StaticObject obj) { + public final double getDoubleVolatile(Object obj) { checkKind(StaticPropertyKind.Double); - return UNSAFE.getDoubleVolatile(obj.getPrimitiveFieldStorage(), offset); + return UNSAFE.getDoubleVolatile(obj, offset); } - public final void setDouble(StaticObject obj, double value) { + public final void setDouble(Object obj, double value) { checkKind(StaticPropertyKind.Double); - UNSAFE.putDouble(obj.getPrimitiveFieldStorage(), (long) offset, value); + UNSAFE.putDouble(obj, (long) offset, value); } - public final void setDoubleVolatile(StaticObject obj, double value) { + public final void setDoubleVolatile(Object obj, double value) { checkKind(StaticPropertyKind.Double); - UNSAFE.putDoubleVolatile(obj.getPrimitiveFieldStorage(), offset, value); + UNSAFE.putDoubleVolatile(obj, offset, value); } // float field access - public final float getFloat(StaticObject obj) { + public final float getFloat(Object obj) { checkKind(StaticPropertyKind.Float); - return UNSAFE.getFloat(obj.getPrimitiveFieldStorage(), (long) offset); + return UNSAFE.getFloat(obj, (long) offset); } - public final float getFloatVolatile(StaticObject obj) { + public final float getFloatVolatile(Object obj) { checkKind(StaticPropertyKind.Float); - return UNSAFE.getFloatVolatile(obj.getPrimitiveFieldStorage(), offset); + return UNSAFE.getFloatVolatile(obj, offset); } - public final void setFloat(StaticObject obj, float value) { + public final void setFloat(Object obj, float value) { checkKind(StaticPropertyKind.Float); - UNSAFE.putFloat(obj.getPrimitiveFieldStorage(), (long) offset, value); + UNSAFE.putFloat(obj, (long) offset, value); } - public final void setFloatVolatile(StaticObject obj, float value) { + public final void setFloatVolatile(Object obj, float value) { checkKind(StaticPropertyKind.Float); - UNSAFE.putFloatVolatile(obj.getPrimitiveFieldStorage(), offset, value); + UNSAFE.putFloatVolatile(obj, offset, value); } // int field access - public final int getInt(StaticObject obj) { + public final int getInt(Object obj) { checkKind(StaticPropertyKind.Int); - return UNSAFE.getInt(obj.getPrimitiveFieldStorage(), (long) offset); + return UNSAFE.getInt(obj, (long) offset); } - public final int getIntVolatile(StaticObject obj) { + public final int getIntVolatile(Object obj) { checkKind(StaticPropertyKind.Int); - return UNSAFE.getIntVolatile(obj.getPrimitiveFieldStorage(), offset); + return UNSAFE.getIntVolatile(obj, offset); } - public final void setInt(StaticObject obj, int value) { + public final void setInt(Object obj, int value) { checkKind(StaticPropertyKind.Int); - UNSAFE.putInt(obj.getPrimitiveFieldStorage(), (long) offset, value); + UNSAFE.putInt(obj, (long) offset, value); } - public final void setIntVolatile(StaticObject obj, int value) { + public final void setIntVolatile(Object obj, int value) { checkKind(StaticPropertyKind.Int); - UNSAFE.putIntVolatile(obj.getPrimitiveFieldStorage(), offset, value); + UNSAFE.putIntVolatile(obj, offset, value); } - public final boolean compareAndSwapInt(StaticObject obj, int before, int after) { + public final boolean compareAndSwapInt(Object obj, int before, int after) { checkKind(StaticPropertyKind.Int); - return UNSAFE.compareAndSwapInt(obj.getPrimitiveFieldStorage(), offset, before, after); + return UNSAFE.compareAndSwapInt(obj, offset, before, after); } - public final int getAndAddInt(StaticObject obj, int value) { + public final int getAndAddInt(Object obj, int value) { checkKind(StaticPropertyKind.Int); - return UNSAFE.getAndAddInt(obj.getPrimitiveFieldStorage(), offset, value); + return UNSAFE.getAndAddInt(obj, offset, value); } - public final int getAndSetInt(StaticObject obj, int value) { + public final int getAndSetInt(Object obj, int value) { checkKind(StaticPropertyKind.Int); - return UNSAFE.getAndSetInt(obj.getPrimitiveFieldStorage(), offset, value); + return UNSAFE.getAndSetInt(obj, offset, value); } // long field access - public final long getLong(StaticObject obj) { + public final long getLong(Object obj) { checkKind(StaticPropertyKind.Long); - return UNSAFE.getLong(obj.getPrimitiveFieldStorage(), (long) offset); + return UNSAFE.getLong(obj, (long) offset); } - public final long getLongVolatile(StaticObject obj) { + public final long getLongVolatile(Object obj) { checkKind(StaticPropertyKind.Long); - return UNSAFE.getLongVolatile(obj.getPrimitiveFieldStorage(), offset); + return UNSAFE.getLongVolatile(obj, offset); } - public final void setLong(StaticObject obj, long value) { + public final void setLong(Object obj, long value) { checkKind(StaticPropertyKind.Long); - UNSAFE.putLong(obj.getPrimitiveFieldStorage(), (long) offset, value); + UNSAFE.putLong(obj, (long) offset, value); } - public final void setLongVolatile(StaticObject obj, long value) { + public final void setLongVolatile(Object obj, long value) { checkKind(StaticPropertyKind.Long); - UNSAFE.putLongVolatile(obj.getPrimitiveFieldStorage(), offset, value); + UNSAFE.putLongVolatile(obj, offset, value); } - public final boolean compareAndSwapLong(StaticObject obj, long before, long after) { + public final boolean compareAndSwapLong(Object obj, long before, long after) { checkKind(StaticPropertyKind.Long); - return UNSAFE.compareAndSwapLong(obj.getPrimitiveFieldStorage(), offset, before, after); + return UNSAFE.compareAndSwapLong(obj, offset, before, after); } - public final long getAndAddLong(StaticObject obj, long value) { + public final long getAndAddLong(Object obj, long value) { checkKind(StaticPropertyKind.Long); - return UNSAFE.getAndAddLong(obj.getPrimitiveFieldStorage(), offset, value); + return UNSAFE.getAndAddLong(obj, offset, value); } - public final long getAndSetLong(StaticObject obj, long value) { + public final long getAndSetLong(Object obj, long value) { checkKind(StaticPropertyKind.Long); - return UNSAFE.getAndSetLong(obj.getPrimitiveFieldStorage(), offset, value); + return UNSAFE.getAndSetLong(obj, offset, value); } // short field access - public final short getShort(StaticObject obj) { + public final short getShort(Object obj) { checkKind(StaticPropertyKind.Short); - return UNSAFE.getShort(obj.getPrimitiveFieldStorage(), (long) offset); + return UNSAFE.getShort(obj, (long) offset); } - public final short getShortVolatile(StaticObject obj) { + public final short getShortVolatile(Object obj) { checkKind(StaticPropertyKind.Short); - return UNSAFE.getShortVolatile(obj.getPrimitiveFieldStorage(), offset); + return UNSAFE.getShortVolatile(obj, offset); } - public final void setShort(StaticObject obj, short value) { + public final void setShort(Object obj, short value) { checkKind(StaticPropertyKind.Short); - UNSAFE.putShort(obj.getPrimitiveFieldStorage(), (long) offset, value); + UNSAFE.putShort(obj, (long) offset, value); } - public final void setShortVolatile(StaticObject obj, short value) { + public final void setShortVolatile(Object obj, short value) { checkKind(StaticPropertyKind.Short); - UNSAFE.putShortVolatile(obj.getPrimitiveFieldStorage(), offset, value); + UNSAFE.putShortVolatile(obj, offset, value); } private static Unsafe getUnsafe() { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java index defca28b5301..737cf3c7d575 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java @@ -286,9 +286,9 @@ private Object getObjectHelper(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getObjectVolatile(obj); + return linkedField.getObjectVolatile(obj.getObjectFieldStorage()); } else { - return linkedField.getObject(obj); + return linkedField.getObject(obj.getObjectFieldStorage()); } } @@ -296,9 +296,9 @@ private void setObjectHelper(StaticObject obj, Object value, boolean forceVolati obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setObjectVolatile(obj, value); + linkedField.setObjectVolatile(obj.getObjectFieldStorage(), value); } else { - linkedField.setObject(obj, value); + linkedField.setObject(obj.getObjectFieldStorage(), value); } } // endregion helper methods @@ -326,14 +326,14 @@ public StaticObject getAndSetObject(StaticObject obj, StaticObject value) { obj.checkNotForeign(); assert !isHidden() : this + " is hidden"; assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); - return (StaticObject) linkedField.getAndSetObject(obj, value); + return (StaticObject) linkedField.getAndSetObject(obj.getObjectFieldStorage(), value); } public boolean compareAndSwapObject(StaticObject obj, Object before, Object after) { obj.checkNotForeign(); assert !isHidden() : this + " is hidden"; assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); - return linkedField.compareAndSwapObject(obj, before, after); + return linkedField.compareAndSwapObject(obj.getObjectFieldStorage(), before, after); } // region hidden Object @@ -366,9 +366,9 @@ public boolean getBoolean(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getBooleanVolatile(obj); + return linkedField.getBooleanVolatile(obj.getPrimitiveFieldStorage()); } else { - return linkedField.getBoolean(obj); + return linkedField.getBoolean(obj.getPrimitiveFieldStorage()); } } @@ -380,9 +380,9 @@ public void setBoolean(StaticObject obj, boolean value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setBooleanVolatile(obj, value); + linkedField.setBooleanVolatile(obj.getPrimitiveFieldStorage(), value); } else { - linkedField.setBoolean(obj, value); + linkedField.setBoolean(obj.getPrimitiveFieldStorage(), value); } } // endregion boolean @@ -396,9 +396,9 @@ public byte getByte(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getByteVolatile(obj); + return linkedField.getByteVolatile(obj.getPrimitiveFieldStorage()); } else { - return linkedField.getByte(obj); + return linkedField.getByte(obj.getPrimitiveFieldStorage()); } } @@ -410,9 +410,9 @@ public void setByte(StaticObject obj, byte value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setByteVolatile(obj, value); + linkedField.setByteVolatile(obj.getPrimitiveFieldStorage(), value); } else { - linkedField.setByte(obj, value); + linkedField.setByte(obj.getPrimitiveFieldStorage(), value); } } // endregion byte @@ -426,9 +426,9 @@ public char getChar(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getCharVolatile(obj); + return linkedField.getCharVolatile(obj.getPrimitiveFieldStorage()); } else { - return linkedField.getChar(obj); + return linkedField.getChar(obj.getPrimitiveFieldStorage()); } } @@ -440,9 +440,9 @@ public void setChar(StaticObject obj, char value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setCharVolatile(obj, value); + linkedField.setCharVolatile(obj.getPrimitiveFieldStorage(), value); } else { - linkedField.setChar(obj, value); + linkedField.setChar(obj.getPrimitiveFieldStorage(), value); } } // endregion char @@ -456,9 +456,9 @@ public double getDouble(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getDoubleVolatile(obj); + return linkedField.getDoubleVolatile(obj.getPrimitiveFieldStorage()); } else { - return linkedField.getDouble(obj); + return linkedField.getDouble(obj.getPrimitiveFieldStorage()); } } @@ -470,9 +470,9 @@ public void setDouble(StaticObject obj, double value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setDoubleVolatile(obj, value); + linkedField.setDoubleVolatile(obj.getPrimitiveFieldStorage(), value); } else { - linkedField.setDouble(obj, value); + linkedField.setDouble(obj.getPrimitiveFieldStorage(), value); } } @@ -487,9 +487,9 @@ public float getFloat(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getFloatVolatile(obj); + return linkedField.getFloatVolatile(obj.getPrimitiveFieldStorage()); } else { - return linkedField.getFloat(obj); + return linkedField.getFloat(obj.getPrimitiveFieldStorage()); } } @@ -501,9 +501,9 @@ public void setFloat(StaticObject obj, float value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setFloatVolatile(obj, value); + linkedField.setFloatVolatile(obj.getPrimitiveFieldStorage(), value); } else { - linkedField.setFloat(obj, value); + linkedField.setFloat(obj.getPrimitiveFieldStorage(), value); } } // endregion float @@ -517,9 +517,9 @@ public int getInt(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getIntVolatile(obj); + return linkedField.getIntVolatile(obj.getPrimitiveFieldStorage()); } else { - return linkedField.getInt(obj); + return linkedField.getInt(obj.getPrimitiveFieldStorage()); } } @@ -531,16 +531,16 @@ public void setInt(StaticObject obj, int value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setIntVolatile(obj, value); + linkedField.setIntVolatile(obj.getPrimitiveFieldStorage(), value); } else { - linkedField.setInt(obj, value); + linkedField.setInt(obj.getPrimitiveFieldStorage(), value); } } public boolean compareAndSwapInt(StaticObject obj, int before, int after) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); - return linkedField.compareAndSwapInt(obj, before, after); + return linkedField.compareAndSwapInt(obj.getPrimitiveFieldStorage(), before, after); } // endregion int @@ -554,9 +554,9 @@ public long getLong(StaticObject obj, boolean forceVolatile) { assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); assert getKind().needsTwoSlots(); if (isVolatile() || forceVolatile) { - return linkedField.getLongVolatile(obj); + return linkedField.getLongVolatile(obj.getPrimitiveFieldStorage()); } else { - return linkedField.getLong(obj); + return linkedField.getLong(obj.getPrimitiveFieldStorage()); } } @@ -569,9 +569,9 @@ public void setLong(StaticObject obj, long value, boolean forceVolatile) { assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); assert getKind().needsTwoSlots(); if (isVolatile() || forceVolatile) { - linkedField.setLongVolatile(obj, value); + linkedField.setLongVolatile(obj.getPrimitiveFieldStorage(), value); } else { - linkedField.setLong(obj, value); + linkedField.setLong(obj.getPrimitiveFieldStorage(), value); } } @@ -579,7 +579,7 @@ public boolean compareAndSwapLong(StaticObject obj, long before, long after) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); assert getKind().needsTwoSlots(); - return linkedField.compareAndSwapLong(obj, before, after); + return linkedField.compareAndSwapLong(obj.getPrimitiveFieldStorage(), before, after); } // endregion long @@ -592,9 +592,9 @@ public short getShort(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getShortVolatile(obj); + return linkedField.getShortVolatile(obj.getPrimitiveFieldStorage()); } else { - return linkedField.getShort(obj); + return linkedField.getShort(obj.getPrimitiveFieldStorage()); } } @@ -606,9 +606,9 @@ public void setShort(StaticObject obj, short value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setShortVolatile(obj, value); + linkedField.setShortVolatile(obj.getPrimitiveFieldStorage(), value); } else { - linkedField.setShort(obj, value); + linkedField.setShort(obj.getPrimitiveFieldStorage(), value); } } // endregion short From 6f0a34c2eedec64692173cac50b1080346c1c71b Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 22 Feb 2021 18:41:32 +0100 Subject: [PATCH 156/290] Generate subtypes of StaticObject for primitive and object field storage. --- espresso/mx.espresso/suite.py | 4 + .../ArrayBasedShapeGenerator.java | 315 +++++++++++++ .../staticobject/ArrayBasedStaticShape.java | 423 +++++++++++++++++ .../staticobject/DefaultStaticObject.java | 29 ++ .../espresso/staticobject/ShapeGenerator.java | 112 +++++ .../espresso/staticobject/StaticProperty.java | 121 +++-- .../staticobject/StaticPropertyKind.java | 5 +- .../espresso/staticobject/StaticShape.java | 58 +++ .../staticobject/StaticShapeBuilder.java | 112 +++++ .../truffle/espresso/impl/ClassRegistry.java | 2 +- .../oracle/truffle/espresso/impl/Field.java | 80 ++-- .../truffle/espresso/impl/LinkedField.java | 8 +- .../truffle/espresso/impl/LinkedKlass.java | 88 ++-- .../espresso/impl/LinkedKlassFieldLayout.java | 437 ++---------------- .../truffle/espresso/impl/ObjectKlass.java | 2 +- .../truffle/espresso/impl/ParserField.java | 4 + .../espresso/runtime/StaticObject.java | 149 +++--- ...oracle_truffle_espresso_InternalUtils.java | 143 +++--- .../substitutions/Target_sun_misc_Unsafe.java | 2 +- 19 files changed, 1402 insertions(+), 692 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticObject.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index 22506444ba4b..d87351bb2245 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -104,6 +104,7 @@ "sourceDirs": ["src"], "dependencies": [ "truffle:TRUFFLE_API", + "truffle:TRUFFLE_ASM_7.2", ], "javaCompliance": "1.8+", "checkstyle": "com.oracle.truffle.espresso", @@ -267,6 +268,9 @@ "truffle:TRUFFLE_NFI_LIBFFI", "tools:TRUFFLE_PROFILER", ], + "exclude": [ + "truffle:TRUFFLE_ASM_7.2", + ], "javaProperties": { "org.graalvm.language.java.home": "", "polyglot.java.JVMLibraryPath": "/truffle", diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java new file mode 100644 index 000000000000..2bd21fd4665d --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; + +import com.oracle.truffle.api.impl.asm.ClassVisitor; +import com.oracle.truffle.api.impl.asm.ClassWriter; +import com.oracle.truffle.api.impl.asm.Label; +import com.oracle.truffle.api.impl.asm.MethodVisitor; +import com.oracle.truffle.api.impl.asm.Type; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; +import org.graalvm.collections.Pair; + +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_FINAL; +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_PUBLIC; +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_SUPER; +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_SYNTHETIC; +import static com.oracle.truffle.api.impl.asm.Opcodes.ACONST_NULL; +import static com.oracle.truffle.api.impl.asm.Opcodes.ALOAD; +import static com.oracle.truffle.api.impl.asm.Opcodes.ANEWARRAY; +import static com.oracle.truffle.api.impl.asm.Opcodes.ARETURN; +import static com.oracle.truffle.api.impl.asm.Opcodes.ASTORE; +import static com.oracle.truffle.api.impl.asm.Opcodes.CHECKCAST; +import static com.oracle.truffle.api.impl.asm.Opcodes.DUP; +import static com.oracle.truffle.api.impl.asm.Opcodes.GETFIELD; +import static com.oracle.truffle.api.impl.asm.Opcodes.GOTO; +import static com.oracle.truffle.api.impl.asm.Opcodes.IFLE; +import static com.oracle.truffle.api.impl.asm.Opcodes.IFNONNULL; +import static com.oracle.truffle.api.impl.asm.Opcodes.ILOAD; +import static com.oracle.truffle.api.impl.asm.Opcodes.INVOKESPECIAL; +import static com.oracle.truffle.api.impl.asm.Opcodes.INVOKEVIRTUAL; +import static com.oracle.truffle.api.impl.asm.Opcodes.NEW; +import static com.oracle.truffle.api.impl.asm.Opcodes.NEWARRAY; +import static com.oracle.truffle.api.impl.asm.Opcodes.PUTFIELD; +import static com.oracle.truffle.api.impl.asm.Opcodes.RETURN; +import static com.oracle.truffle.api.impl.asm.Opcodes.T_BYTE; +import static com.oracle.truffle.api.impl.asm.Opcodes.V1_8; + +final class ArrayBasedShapeGenerator extends ShapeGenerator { + + private static final ConcurrentHashMap, Class>, ArrayBasedShapeGenerator> generatorCache = new ConcurrentHashMap<>(); + + private final int byteArrayOffset; + private final int objectArrayOffset; + + private ArrayBasedShapeGenerator(ArrayBasedShapeGenerator rootSG, Collection extendedProperties, StaticShape parentShape) { + super(rootSG.generatedStorageClass, rootSG.generatedFactoryClass, extendedProperties, parentShape); + byteArrayOffset = rootSG.byteArrayOffset; + objectArrayOffset = rootSG.objectArrayOffset; + } + + private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties) { + super(generatedStorageClass, generatedFactoryClass, extendedProperties, null); + byteArrayOffset = getObjectFieldOffset(generatedStorageClass, "primitive"); + objectArrayOffset = getObjectFieldOffset(generatedStorageClass, "object"); + } + + @SuppressWarnings("unchecked") + static ArrayBasedShapeGenerator getShapeGenerator(StaticShape parentShape, Collection extendedProperties) { + Class parentStorageSuperClass = parentShape.getStorageClass().getSuperclass(); + Class parentStorageFactoryInterface = parentShape.getFactoryInterface(); + ArrayBasedShapeGenerator rootSG = (ArrayBasedShapeGenerator) generatorCache.get(Pair.create(parentStorageSuperClass, parentStorageFactoryInterface)); + return new ArrayBasedShapeGenerator<>(rootSG, extendedProperties, parentShape); + } + + @SuppressWarnings("unchecked") + static ArrayBasedShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface, Collection extendedProperties) { + Pair, Class> pair = Pair.create(storageSuperClass, storageFactoryInterface); + ArrayBasedShapeGenerator sg = (ArrayBasedShapeGenerator) generatorCache.get(pair); + if (sg == null) { + Class generatedStorageClass = generateStorage(storageSuperClass); + Class generatedFactoryClass = generateFactory(generatedStorageClass, storageFactoryInterface); + sg = new ArrayBasedShapeGenerator<>(generatedStorageClass, generatedFactoryClass, extendedProperties); + ArrayBasedShapeGenerator prevSg = (ArrayBasedShapeGenerator) generatorCache.putIfAbsent(pair, sg); + if (prevSg != null) { + sg = prevSg; + } + } else { + sg = new ArrayBasedShapeGenerator<>(sg, extendedProperties, null); + } + return sg; + } + + private static int getObjectFieldOffset(Class c, String fieldName) { + try { + return Math.toIntExact(UNSAFE.objectFieldOffset(c.getField(fieldName))); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + @Override + StaticShape generateShape() { + return ArrayBasedStaticShape.create(generatedStorageClass, generatedFactoryClass, parentShape, extendedProperties, byteArrayOffset, objectArrayOffset); + } + + private static String getStorageConstructorDescriptor(Constructor superConstructor) { + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (Class parameter : superConstructor.getParameterTypes()) { + sb.append(Type.getDescriptor(parameter)); + } + sb.append("II"); + return sb.append(")V").toString(); + } + + private static void addStorageConstructors(ClassVisitor cv, String storageName, Class storageSuperClass, String storageSuperName) { + for (Constructor superConstructor : storageSuperClass.getDeclaredConstructors()) { + String storageConstructorDescriptor = getStorageConstructorDescriptor(superConstructor); + String superConstructorDescriptor = Type.getConstructorDescriptor(superConstructor); + + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", storageConstructorDescriptor, null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + int var = 1; + for (Class constructorParameter : superConstructor.getParameterTypes()) { + int loadOpcode = Type.getType(constructorParameter).getOpcode(ILOAD); + mv.visitVarInsn(loadOpcode, var++); + } + mv.visitMethodInsn(INVOKESPECIAL, storageSuperName, "", superConstructorDescriptor, false); + + // primitive = primitiveArraySize > 0 ? new byte[primitiveArraySize] : null; + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, var); + Label label2 = new Label(); + mv.visitJumpInsn(IFLE, label2); + mv.visitVarInsn(ILOAD, var); + mv.visitIntInsn(NEWARRAY, T_BYTE); + Label label3 = new Label(); + mv.visitJumpInsn(GOTO, label3); + mv.visitLabel(label2); + mv.visitInsn(ACONST_NULL); + mv.visitLabel(label3); + mv.visitFieldInsn(PUTFIELD, storageName, "primitive", "[B"); + + // object = objectArraySize > 0 ? new Object[objectArraySize] : null; + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, var + 1); + Label label5 = new Label(); + mv.visitJumpInsn(IFLE, label5); + mv.visitVarInsn(ILOAD, var + 1); + mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); + Label label6 = new Label(); + mv.visitJumpInsn(GOTO, label6); + mv.visitLabel(label5); + mv.visitInsn(ACONST_NULL); + mv.visitLabel(label6); + mv.visitFieldInsn(PUTFIELD, storageName, "object", "[Ljava/lang/Object;"); + + mv.visitInsn(RETURN); + mv.visitMaxs(Math.max(var, 2), var + 2); + + mv.visitEnd(); + } + } + + private static void addCloneMethod(ClassVisitor cv, String className) { + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "clone", "()Ljava/lang/Object;", null, new String[]{"java/lang/CloneNotSupportedException"}); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "clone", "()Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, className); + mv.visitVarInsn(ASTORE, 1); + + // clone.primitive = (primitive == null ? null : (byte[]) primitive.clone()); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "primitive", "[B"); + Label label2 = new Label(); + mv.visitJumpInsn(IFNONNULL, label2); + mv.visitInsn(ACONST_NULL); + Label label3 = new Label(); + mv.visitJumpInsn(GOTO, label3); + mv.visitLabel(label2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "primitive", "[B"); + mv.visitMethodInsn(INVOKEVIRTUAL, "[B", "clone", "()Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, "[B"); + mv.visitTypeInsn(CHECKCAST, "[B"); + mv.visitLabel(label3); + mv.visitFieldInsn(PUTFIELD, className, "primitive", "[B"); + + // clone.object = (object == null ? null : (Object[]) object.clone()); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "object", "[Ljava/lang/Object;"); + Label label5 = new Label(); + mv.visitJumpInsn(IFNONNULL, label5); + mv.visitInsn(ACONST_NULL); + Label label6 = new Label(); + mv.visitJumpInsn(GOTO, label6); + mv.visitLabel(label5); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "object", "[Ljava/lang/Object;"); + mv.visitMethodInsn(INVOKEVIRTUAL, "[Ljava/lang/Object;", "clone", "()Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;"); + mv.visitLabel(label6); + mv.visitFieldInsn(PUTFIELD, className, "object", "[Ljava/lang/Object;"); + + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + + private static void addFactoryFields(ClassVisitor cv) { + cv.visitField(ACC_PUBLIC | ACC_FINAL, "primitiveArraySize", "I", null, null).visitEnd(); + cv.visitField(ACC_PUBLIC | ACC_FINAL, "objectArraySize", "I", null, null).visitEnd(); + } + + private static void addFactoryConstructor(ClassVisitor cv, String className) { + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", "(II)V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 1); + mv.visitFieldInsn(PUTFIELD, className, "primitiveArraySize", "I"); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 2); + mv.visitFieldInsn(PUTFIELD, className, "objectArraySize", "I"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 4); + mv.visitEnd(); + } + + private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Class storageFactoryInterface, String factoryName) { + for (Method m : storageFactoryInterface.getMethods()) { + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_FINAL, m.getName(), Type.getMethodDescriptor(m), null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, Type.getInternalName(storageClass)); + mv.visitInsn(DUP); + int var = 1; + StringBuilder constructorDescriptor = new StringBuilder(); + constructorDescriptor.append('('); + for (Class p : m.getParameterTypes()) { + int loadOpcode = Type.getType(p).getOpcode(ILOAD); + mv.visitVarInsn(loadOpcode, var++); + constructorDescriptor.append(Type.getDescriptor(p)); + } + + for (String fieldName : new String[]{"primitiveArraySize", "objectArraySize"}) { + constructorDescriptor.append("I"); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, factoryName, fieldName, "I"); + var++; + } + + constructorDescriptor.append(")V"); + String storageName = Type.getInternalName(storageClass); + mv.visitMethodInsn(INVOKESPECIAL, storageName, "", constructorDescriptor.toString(), false); + mv.visitInsn(ARETURN); + mv.visitMaxs(var, 1); + mv.visitEnd(); + } + } + + private static Class generateStorage(Class storageSuperClass) { + String storageSuperName = Type.getInternalName(storageSuperClass); + String storageName = generateStorageName(storageSuperClass); + Collection arrayProperties = Arrays.asList( + new ExtendedProperty(new StaticProperty(StaticPropertyKind.BYTE_ARRAY), "primitive", true), + new ExtendedProperty(new StaticProperty(StaticPropertyKind.OBJECT_ARRAY), "object", true)); + + int classWriterFlags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS; + ClassWriter storageWriter = new ClassWriter(classWriterFlags); + int storageAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC; + storageWriter.visit(V1_8, storageAccess, storageName, null, storageSuperName, null); + addStorageConstructors(storageWriter, storageName, storageSuperClass, storageSuperName); + addStorageFields(storageWriter, arrayProperties); + if (Cloneable.class.isAssignableFrom(storageSuperClass)) { + addCloneMethod(storageWriter, storageName); + } + storageWriter.visitEnd(); + return load(storageName, storageWriter.toByteArray(), storageSuperClass); + } + + @SuppressWarnings("unchecked") + private static Class generateFactory(Class storageClass, Class storageFactoryInterface) { + ClassWriter factoryWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); + int factoryAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC | ACC_FINAL; + String factoryName = generateFactoryName(storageClass); + factoryWriter.visit(V1_8, factoryAccess, factoryName, null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(storageFactoryInterface)}); + addFactoryFields(factoryWriter); + addFactoryConstructor(factoryWriter, factoryName); + addFactoryMethods(factoryWriter, storageClass, storageFactoryInterface, factoryName); + factoryWriter.visitEnd(); + return (Class) load(factoryName, factoryWriter.toByteArray(), storageClass); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java new file mode 100644 index 000000000000..719c04aaac9a --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import sun.misc.Unsafe; + +import static com.oracle.truffle.espresso.staticobject.StaticPropertyKind.N_PRIMITIVES; + +final class ArrayBasedStaticShape extends StaticShape { + private static final Unsafe UNSAFE; + static { + UNSAFE = getUnsafe(); + } + + private final ArrayBasedPropertyLayout propertyLayout; + + private ArrayBasedStaticShape(Class storageClass, T factory, ArrayBasedPropertyLayout propertyLayout) { + super(storageClass, factory); + this.propertyLayout = propertyLayout; + } + + static ArrayBasedStaticShape create(Class generatedStorageClass, Class generatedFactoryClass, StaticShape parentShape, Collection extendedProperties, + int byteArrayOffset, int objectArrayOffset) { + try { + ArrayBasedPropertyLayout parentPropertyLayout = parentShape == null ? null : ((ArrayBasedStaticShape) parentShape).getPropertyLayout(); + ArrayBasedPropertyLayout propertyLayout = new ArrayBasedPropertyLayout(parentPropertyLayout, extendedProperties, byteArrayOffset, objectArrayOffset); + T factory = generatedFactoryClass.cast(generatedFactoryClass.getConstructor(int.class, int.class).newInstance(propertyLayout.getPrimitiveArraySize(), propertyLayout.getObjectArraySize())); + return new ArrayBasedStaticShape<>(generatedStorageClass, factory, propertyLayout); + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + Object getStorage(Object obj, boolean primitive) { + Object receiverObject = cast(obj, storageClass); + return UNSAFE.getObject(receiverObject, (long) (primitive ? propertyLayout.byteArrayOffset : propertyLayout.objectArrayOffset)); + } + + private ArrayBasedPropertyLayout getPropertyLayout() { + return propertyLayout; + } + + private static Unsafe getUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException e) { + } + try { + Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafeInstance.setAccessible(true); + return (Unsafe) theUnsafeInstance.get(Unsafe.class); + } catch (Exception e) { + throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); + } + } + + /** + *

+ * Creates a layout for the primitive fields of a given class, and assigns to each field the raw + * offset in the byte array that represents the data. The layout tries to be as compact as + * possible. The rules for determining the layout are as follow: + * + *

  • The Top klass (j.l.Object) will have its field offset corresponding the point where the + * data in the byte array begins (the first offset after the array header)
  • + *
  • If this offset is not long-aligned, then start further so that this new offset is aligned + * to a long. Register that there is some space between the start of the raw data and the first + * field offset (perhaps a byte could be squeezed in).
  • + *
  • Other klasses will inherit their super klass' layout, and start appending their own + * declared field at the first offset aligned with the biggest primitive a given class has.
  • + *
  • If there are known holes in the parents layout, the klass will attempt to squeeze its own + * fields in these holes.
  • + *

    + *

    + * For example, suppose we have the following hierarchy, and that the first offset of the data + * in a byte array is at 14: + *

    + * + *
    +     * class A {
    +     *     long l;
    +     *     int i;
    +     * }
    +     *
    +     * class B extends A {
    +     *     double d;
    +     * }
    +     *
    +     * class C extends B {
    +     *     float f;
    +     *     short s;
    +     * }
    +     * 
    + * + * Then, the resulting layout for A would be: + * + *
    +     * - 0-13: header
    +     * - 14-15: unused  -> Padding for aligned long
    +     * - 16-23: l
    +     * - 24-27: i
    +     * 
    + * + * the resulting layout for B would be: + * + *
    +     * - 0-13: header   }
    +     * - 14-15: unused  }   Same as
    +     * - 16-23: l       }      A
    +     * - 24-27: i       }
    +     * - 28-31: unused  -> Padding for aligned double
    +     * - 32-39: d
    +     * 
    + * + * the resulting layout for C would be: + * + *
    +     * - 0-13: header
    +     * - 14-15: s       ->   hole filled
    +     * - 16-23: l
    +     * - 24-27: i
    +     * - 28-31: f       ->   hole filled
    +     * - 32-39: d
    +     * 
    + */ + static class ArrayBasedPropertyLayout { + private final int primitiveArraySize; + private final int objectArraySize; + + // Stats about primitive fields + @CompilationFinal(dimensions = 2) // + private final int[][] leftoverHoles; + private final int lastOffset; + + private final int byteArrayOffset; + private final int objectArrayOffset; + + ArrayBasedPropertyLayout(ArrayBasedPropertyLayout parentLayout, Collection extendedProperties, int byteArrayOffset, int objectArrayOffset) { + this.byteArrayOffset = byteArrayOffset; + this.objectArrayOffset = objectArrayOffset; + + // Stats about primitive fields + int superTotalByteCount; + int[][] parentLeftoverHoles; + int objArraySize; + if (parentLayout == null) { + // Align the starting offset to a long. + superTotalByteCount = base() + alignmentCorrection(); + // Register a hole if we had to realign. + if (alignmentCorrection() > 0) { + parentLeftoverHoles = new int[][]{{base(), base() + alignmentCorrection()}}; + } else { + parentLeftoverHoles = new int[0][]; + } + objArraySize = 0; + } else { + superTotalByteCount = parentLayout.lastOffset; + parentLeftoverHoles = parentLayout.leftoverHoles; + objArraySize = parentLayout.objectArraySize; + } + + int[] primitiveFields = new int[N_PRIMITIVES]; + for (ExtendedProperty extendedProperty : extendedProperties) { + byte propertyKind = extendedProperty.getProperty().getInternalKind(); + if (propertyKind != StaticPropertyKind.Object.toByte()) { + primitiveFields[propertyKind]++; + } + } + + PrimitiveFieldIndexes primitiveFieldIndexes = new PrimitiveFieldIndexes(primitiveFields, superTotalByteCount, parentLeftoverHoles); + for (ExtendedProperty extendedProperty : extendedProperties) { + byte propertyKind = extendedProperty.getProperty().getInternalKind(); + int index; + if (propertyKind == StaticPropertyKind.Object.toByte()) { + index = Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * objArraySize++; + } else { + index = primitiveFieldIndexes.getIndex(propertyKind); + } + extendedProperty.getProperty().initOffset(index); + } + lastOffset = primitiveFieldIndexes.offsets[N_PRIMITIVES - 1]; + primitiveArraySize = getSizeToAlloc(parentLayout == null ? 0 : parentLayout.primitiveArraySize, primitiveFieldIndexes); + objectArraySize = objArraySize; + leftoverHoles = primitiveFieldIndexes.schedule.nextLeftoverHoles; + } + + /* + * If the object model does not start on a long-aligned offset. To manage, we will align our + * indexes to the actual relative address to the start of the object. Note that we still + * make a pretty strong assumption here: All arrays are allocated at an address aligned with + * a *long* + * + * Note that we cannot use static final fields here, as SVM initializes them at build time + * using HotSpot's values, and thus the values for base and alignment would not be correct. + */ + private static int base() { + return Unsafe.ARRAY_BYTE_BASE_OFFSET; + } + + private static int alignmentCorrection() { + int misalignment = Unsafe.ARRAY_BYTE_BASE_OFFSET % Unsafe.ARRAY_LONG_INDEX_SCALE; + return misalignment == 0 ? 0 : Unsafe.ARRAY_LONG_INDEX_SCALE - misalignment; + } + + private static int getSizeToAlloc(int superToAlloc, PrimitiveFieldIndexes fieldIndexes) { + int toAlloc = fieldIndexes.offsets[N_PRIMITIVES - 1] - base(); + assert toAlloc >= 0; + if (toAlloc == alignmentCorrection() && fieldIndexes.schedule.isEmpty()) { + // If superKlass has fields in the alignment hole, we will need to allocate. If not, + // we + // can save an array. Note that if such a field exists, we will allocate an array of + // size at least the alignment correction, since we fill holes from the right to the + // left. + toAlloc = superToAlloc; + } + return toAlloc; + } + + int getPrimitiveArraySize() { + return primitiveArraySize; + } + + int getObjectArraySize() { + return objectArraySize; + } + + private static final class PrimitiveFieldIndexes { + final int[] offsets; + final FillingSchedule schedule; + + // To ignore leftoverHoles, pass FillingSchedule.EMPTY_INT_ARRAY_ARRAY. + // This is used for static fields, where the gain would be negligible. + PrimitiveFieldIndexes(int[] primitiveFields, int superTotalByteCount, int[][] leftoverHoles) { + offsets = new int[N_PRIMITIVES]; + offsets[0] = startOffset(superTotalByteCount, primitiveFields); + this.schedule = FillingSchedule.create(superTotalByteCount, offsets[0], primitiveFields, leftoverHoles); + // FillingSchedule.create() modifies primitiveFields. + // Only offsets[0] must be initialized before creating the filling schedule. + for (int i = 1; i < N_PRIMITIVES; i++) { + offsets[i] = offsets[i - 1] + primitiveFields[i - 1] * StaticPropertyKind.getByteCount(i - 1); + } + } + + int getIndex(byte propertyKind) { + ScheduleEntry entry = schedule.query(propertyKind); + if (entry != null) { + return entry.offset; + } else { + int prevOffset = offsets[propertyKind]; + offsets[propertyKind] += StaticPropertyKind.getByteCount(propertyKind); + return prevOffset; + } + } + + // Find first primitive to set, and align on it. + private static int startOffset(int superTotalByteCount, int[] primitiveCounts) { + int i = 0; + while (i < N_PRIMITIVES && primitiveCounts[i] == 0) { + i++; + } + if (i == N_PRIMITIVES) { + return superTotalByteCount; + } + int r = superTotalByteCount % StaticPropertyKind.getByteCount(i); + if (r == 0) { + return superTotalByteCount; + } + return superTotalByteCount + StaticPropertyKind.getByteCount(i) - r; + } + } + + /** + * Greedily tries to fill the space between a parent's fields and its child. + */ + private static final class FillingSchedule { + static final int[][] EMPTY_INT_ARRAY_ARRAY = new int[0][]; + + final List schedule; + int[][] nextLeftoverHoles; + + final boolean isEmpty; + + boolean isEmpty() { + return isEmpty; + } + + static FillingSchedule create(int holeStart, int holeEnd, int[] counts, int[][] leftoverHoles) { + List schedule = new ArrayList<>(); + if (leftoverHoles == EMPTY_INT_ARRAY_ARRAY) { + // packing static fields is not as interesting as instance fields: the array + // created + // to remember the hole would be bigger than what we would gain. Only schedule + // for + // direct parent. + scheduleHole(holeStart, holeEnd, counts, schedule); + return new FillingSchedule(schedule); + } else { + List nextHoles = new ArrayList<>(); + scheduleHole(holeStart, holeEnd, counts, schedule, nextHoles); + if (leftoverHoles != null) { + for (int[] hole : leftoverHoles) { + scheduleHole(hole[0], hole[1], counts, schedule, nextHoles); + } + } + return new FillingSchedule(schedule, nextHoles); + } + } + + private static void scheduleHole(int holeStart, int holeEnd, int[] counts, List schedule, List nextHoles) { + int end = holeEnd; + int holeSize = holeEnd - holeStart; + byte i = 0; + + mainloop: while (holeSize > 0 && i < N_PRIMITIVES) { + int byteCount = StaticPropertyKind.getByteCount(i); + while (counts[i] > 0 && byteCount <= holeSize) { + int newEnd = end - byteCount; + if (newEnd % byteCount != 0) { + int misalignment = newEnd % byteCount; + int aligned = newEnd - misalignment; + if (aligned < holeStart) { + // re-aligning the store makes it overlap with somethig else: abort. + i++; + continue mainloop; + } + schedule.add(new ScheduleEntry(i, aligned)); + counts[i]--; + // We created a new hole of size `misaligned`. Try to fill it. + scheduleHole(end - misalignment, end, counts, schedule, nextHoles); + newEnd = aligned; + } else { + counts[i]--; + schedule.add(new ScheduleEntry(i, newEnd)); + } + end = newEnd; + holeSize = end - holeStart; + } + i++; + } + if (holeSize > 0) { + nextHoles.add(new int[]{holeStart, end}); + } + } + + private static void scheduleHole(int holeStart, int holeEnd, int[] counts, List schedule) { + int end = holeEnd; + int holeSize = holeEnd - holeStart; + byte i = 0; + + while (holeSize > 0 && i < N_PRIMITIVES) { + int primitiveByteCount = StaticPropertyKind.getByteCount(i); + while (counts[i] > 0 && primitiveByteCount <= holeSize) { + counts[i]--; + end -= primitiveByteCount; + holeSize -= primitiveByteCount; + schedule.add(new ScheduleEntry(i, end)); + } + i++; + } + assert holeSize >= 0; + } + + private FillingSchedule(List schedule) { + this.schedule = schedule; + this.isEmpty = schedule == null || schedule.isEmpty(); + } + + private FillingSchedule(List schedule, List nextHoles) { + this.schedule = schedule; + this.nextLeftoverHoles = nextHoles.isEmpty() ? null : nextHoles.toArray(EMPTY_INT_ARRAY_ARRAY); + this.isEmpty = schedule != null && schedule.isEmpty(); + } + + ScheduleEntry query(byte propertyKind) { + for (ScheduleEntry e : schedule) { + if (e.propertyKind == propertyKind) { + schedule.remove(e); + return e; + } + } + return null; + } + } + + private static class ScheduleEntry { + final byte propertyKind; + final int offset; + + ScheduleEntry(byte propertyKind, int offset) { + this.propertyKind = propertyKind; + this.offset = offset; + } + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticObject.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticObject.java new file mode 100644 index 000000000000..f47c3bfa974e --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticObject.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +public class DefaultStaticObject { + public interface DefaultStaticObjectFactory { + DefaultStaticObject create(); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java new file mode 100644 index 000000000000..573d0307c6e4 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +import com.oracle.truffle.api.impl.asm.ClassVisitor; +import com.oracle.truffle.api.impl.asm.FieldVisitor; +import com.oracle.truffle.api.impl.asm.Type; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; +import sun.misc.Unsafe; + +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_FINAL; +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_PUBLIC; + +abstract class ShapeGenerator { + protected static final Unsafe UNSAFE = getUnsafe(); + private static final String DELIMITER = "$$"; + private static final AtomicInteger counter = new AtomicInteger(); + + protected final Class generatedStorageClass; + protected final Class generatedFactoryClass; + protected final Collection extendedProperties; + protected final StaticShape parentShape; + + ShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, StaticShape parentShape) { + this.generatedStorageClass = generatedStorageClass; + this.generatedFactoryClass = generatedFactoryClass; + this.extendedProperties = extendedProperties; + this.parentShape = parentShape; + } + + abstract StaticShape generateShape(); + + @SuppressWarnings("unchecked") + static ShapeGenerator getShapeGenerator(StaticShape parentShape, Collection extendedProperties) { + return ArrayBasedShapeGenerator.getShapeGenerator(parentShape, extendedProperties); + } + + @SuppressWarnings("unchecked") + static ShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface, Collection extendedProperties) { + return ArrayBasedShapeGenerator.getShapeGenerator(storageSuperClass, storageFactoryInterface, extendedProperties); + } + + static String generateStorageName(Class storageSuperClass) { + String internalStorageSuperClassName = Type.getInternalName(storageSuperClass); + String baseName; + int index = internalStorageSuperClassName.indexOf(DELIMITER); + if (index == -1) { + baseName = internalStorageSuperClassName + DELIMITER; + } else { + baseName = internalStorageSuperClassName.substring(0, index + DELIMITER.length()); + } + return baseName + counter.incrementAndGet(); + } + + static String generateFactoryName(Class generatedStorageClass) { + return Type.getInternalName(generatedStorageClass) + DELIMITER + "Factory"; + } + + static void addStorageFields(ClassVisitor cv, Collection extendedProperties) { + for (ExtendedProperty extendedProperty : extendedProperties) { + int access = ACC_PUBLIC; + if (extendedProperty.isFinal()) { + access |= ACC_FINAL; + } + FieldVisitor fv = cv.visitField(access, extendedProperty.getName(), StaticPropertyKind.getDescriptor(extendedProperty.getProperty().getInternalKind()), null, null); + fv.visitEnd(); + } + } + + @SuppressWarnings("unchecked") + static Class load(String name, byte[] bytes, Class referenceClass) { + return (Class) UNSAFE.defineClass(name, bytes, 0, bytes.length, referenceClass.getClassLoader(), referenceClass.getProtectionDomain()); + } + + private static Unsafe getUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException e) { + } + try { + Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafeInstance.setAccessible(true); + return (Unsafe) theUnsafeInstance.get(Unsafe.class); + } catch (Exception e) { + throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index fac864f1ac3f..9cb8bec8a2cc 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -23,23 +23,44 @@ package com.oracle.truffle.espresso.staticobject; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import java.lang.reflect.Field; import sun.misc.Unsafe; public class StaticProperty { private static final Unsafe UNSAFE = getUnsafe(); private final byte internalKind; - private final int offset; + @CompilationFinal // + private StaticShape shape; + @CompilationFinal // + private int offset; - protected StaticProperty(StaticPropertyKind kind, int offset) { - this.internalKind = getInternalKind(kind); - this.offset = offset; + StaticProperty(byte internalKind) { + this.internalKind = internalKind; + } + + protected StaticProperty(StaticPropertyKind kind) { + this(getInternalKind(kind)); } private static byte getInternalKind(StaticPropertyKind kind) { return kind.toByte(); } + void initOffset(int o) { + if (this.offset != 0) { + throw new RuntimeException("Attempt to reinitialize the offset of a static property. Was it added to more than one builder?"); + } + this.offset = o; + } + + void initShape(StaticShape s) { + if (this.shape != null) { + throw new RuntimeException("Attempt to reinitialize the shape of a static property. Was it added to more than one builder?"); + } + this.shape = s; + } + /** * The offset is the actual position in the field array of an actual instance. */ @@ -47,6 +68,10 @@ public int getOffset() { return offset; } + byte getInternalKind() { + return internalKind; + } + private void checkKind(StaticPropertyKind kind) { if (this.internalKind != getInternalKind(kind)) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -58,230 +83,230 @@ private void checkKind(StaticPropertyKind kind) { // Object field access public final Object getObject(Object obj) { checkKind(StaticPropertyKind.Object); - return UNSAFE.getObject(obj, (long) offset); + return UNSAFE.getObject(shape.getStorage(obj, false), (long) offset); } public final Object getObjectVolatile(Object obj) { checkKind(StaticPropertyKind.Object); - return UNSAFE.getObjectVolatile(obj, offset); + return UNSAFE.getObjectVolatile(shape.getStorage(obj, false), offset); } public final void setObject(Object obj, Object value) { checkKind(StaticPropertyKind.Object); - UNSAFE.putObject(obj, (long) offset, value); + UNSAFE.putObject(shape.getStorage(obj, false), (long) offset, value); } public final void setObjectVolatile(Object obj, Object value) { checkKind(StaticPropertyKind.Object); - UNSAFE.putObjectVolatile(obj, offset, value); + UNSAFE.putObjectVolatile(shape.getStorage(obj, false), offset, value); } public final boolean compareAndSwapObject(Object obj, Object before, Object after) { checkKind(StaticPropertyKind.Object); - return UNSAFE.compareAndSwapObject(obj, offset, before, after); + return UNSAFE.compareAndSwapObject(shape.getStorage(obj, false), offset, before, after); } public final Object getAndSetObject(Object obj, Object value) { checkKind(StaticPropertyKind.Object); - return UNSAFE.getAndSetObject(obj, offset, value); + return UNSAFE.getAndSetObject(shape.getStorage(obj, false), offset, value); } // boolean field access public final boolean getBoolean(Object obj) { checkKind(StaticPropertyKind.Boolean); - return UNSAFE.getBoolean(obj, (long) offset); + return UNSAFE.getBoolean(shape.getStorage(obj, true), (long) offset); } public final boolean getBooleanVolatile(Object obj) { checkKind(StaticPropertyKind.Boolean); - return UNSAFE.getBooleanVolatile(obj, offset); + return UNSAFE.getBooleanVolatile(shape.getStorage(obj, true), offset); } public final void setBoolean(Object obj, boolean value) { checkKind(StaticPropertyKind.Boolean); - UNSAFE.putBoolean(obj, (long) offset, value); + UNSAFE.putBoolean(shape.getStorage(obj, true), (long) offset, value); } public final void setBooleanVolatile(Object obj, boolean value) { checkKind(StaticPropertyKind.Boolean); - UNSAFE.putBooleanVolatile(obj, offset, value); + UNSAFE.putBooleanVolatile(shape.getStorage(obj, true), offset, value); } // byte field access public final byte getByte(Object obj) { checkKind(StaticPropertyKind.Byte); - return UNSAFE.getByte(obj, (long) offset); + return UNSAFE.getByte(shape.getStorage(obj, true), (long) offset); } public final byte getByteVolatile(Object obj) { checkKind(StaticPropertyKind.Byte); - return UNSAFE.getByteVolatile(obj, offset); + return UNSAFE.getByteVolatile(shape.getStorage(obj, true), offset); } public final void setByte(Object obj, byte value) { checkKind(StaticPropertyKind.Byte); - UNSAFE.putByte(obj, (long) offset, value); + UNSAFE.putByte(shape.getStorage(obj, true), (long) offset, value); } public final void setByteVolatile(Object obj, byte value) { checkKind(StaticPropertyKind.Byte); - UNSAFE.putByteVolatile(obj, offset, value); + UNSAFE.putByteVolatile(shape.getStorage(obj, true), offset, value); } // char field access public final char getChar(Object obj) { checkKind(StaticPropertyKind.Char); - return UNSAFE.getChar(obj, (long) offset); + return UNSAFE.getChar(shape.getStorage(obj, true), (long) offset); } public final char getCharVolatile(Object obj) { checkKind(StaticPropertyKind.Char); - return UNSAFE.getCharVolatile(obj, offset); + return UNSAFE.getCharVolatile(shape.getStorage(obj, true), offset); } public final void setChar(Object obj, char value) { checkKind(StaticPropertyKind.Char); - UNSAFE.putChar(obj, (long) offset, value); + UNSAFE.putChar(shape.getStorage(obj, true), (long) offset, value); } public final void setCharVolatile(Object obj, char value) { checkKind(StaticPropertyKind.Char); - UNSAFE.putCharVolatile(obj, offset, value); + UNSAFE.putCharVolatile(shape.getStorage(obj, true), offset, value); } // double field access public final double getDouble(Object obj) { checkKind(StaticPropertyKind.Double); - return UNSAFE.getDouble(obj, (long) offset); + return UNSAFE.getDouble(shape.getStorage(obj, true), (long) offset); } public final double getDoubleVolatile(Object obj) { checkKind(StaticPropertyKind.Double); - return UNSAFE.getDoubleVolatile(obj, offset); + return UNSAFE.getDoubleVolatile(shape.getStorage(obj, true), offset); } public final void setDouble(Object obj, double value) { checkKind(StaticPropertyKind.Double); - UNSAFE.putDouble(obj, (long) offset, value); + UNSAFE.putDouble(shape.getStorage(obj, true), (long) offset, value); } public final void setDoubleVolatile(Object obj, double value) { checkKind(StaticPropertyKind.Double); - UNSAFE.putDoubleVolatile(obj, offset, value); + UNSAFE.putDoubleVolatile(shape.getStorage(obj, true), offset, value); } // float field access public final float getFloat(Object obj) { checkKind(StaticPropertyKind.Float); - return UNSAFE.getFloat(obj, (long) offset); + return UNSAFE.getFloat(shape.getStorage(obj, true), (long) offset); } public final float getFloatVolatile(Object obj) { checkKind(StaticPropertyKind.Float); - return UNSAFE.getFloatVolatile(obj, offset); + return UNSAFE.getFloatVolatile(shape.getStorage(obj, true), offset); } public final void setFloat(Object obj, float value) { checkKind(StaticPropertyKind.Float); - UNSAFE.putFloat(obj, (long) offset, value); + UNSAFE.putFloat(shape.getStorage(obj, true), (long) offset, value); } public final void setFloatVolatile(Object obj, float value) { checkKind(StaticPropertyKind.Float); - UNSAFE.putFloatVolatile(obj, offset, value); + UNSAFE.putFloatVolatile(shape.getStorage(obj, true), offset, value); } // int field access public final int getInt(Object obj) { checkKind(StaticPropertyKind.Int); - return UNSAFE.getInt(obj, (long) offset); + return UNSAFE.getInt(shape.getStorage(obj, true), (long) offset); } public final int getIntVolatile(Object obj) { checkKind(StaticPropertyKind.Int); - return UNSAFE.getIntVolatile(obj, offset); + return UNSAFE.getIntVolatile(shape.getStorage(obj, true), offset); } public final void setInt(Object obj, int value) { checkKind(StaticPropertyKind.Int); - UNSAFE.putInt(obj, (long) offset, value); + UNSAFE.putInt(shape.getStorage(obj, true), (long) offset, value); } public final void setIntVolatile(Object obj, int value) { checkKind(StaticPropertyKind.Int); - UNSAFE.putIntVolatile(obj, offset, value); + UNSAFE.putIntVolatile(shape.getStorage(obj, true), offset, value); } public final boolean compareAndSwapInt(Object obj, int before, int after) { checkKind(StaticPropertyKind.Int); - return UNSAFE.compareAndSwapInt(obj, offset, before, after); + return UNSAFE.compareAndSwapInt(shape.getStorage(obj, true), offset, before, after); } public final int getAndAddInt(Object obj, int value) { checkKind(StaticPropertyKind.Int); - return UNSAFE.getAndAddInt(obj, offset, value); + return UNSAFE.getAndAddInt(shape.getStorage(obj, true), offset, value); } public final int getAndSetInt(Object obj, int value) { checkKind(StaticPropertyKind.Int); - return UNSAFE.getAndSetInt(obj, offset, value); + return UNSAFE.getAndSetInt(shape.getStorage(obj, true), offset, value); } // long field access public final long getLong(Object obj) { checkKind(StaticPropertyKind.Long); - return UNSAFE.getLong(obj, (long) offset); + return UNSAFE.getLong(shape.getStorage(obj, true), (long) offset); } public final long getLongVolatile(Object obj) { checkKind(StaticPropertyKind.Long); - return UNSAFE.getLongVolatile(obj, offset); + return UNSAFE.getLongVolatile(shape.getStorage(obj, true), offset); } public final void setLong(Object obj, long value) { checkKind(StaticPropertyKind.Long); - UNSAFE.putLong(obj, (long) offset, value); + UNSAFE.putLong(shape.getStorage(obj, true), (long) offset, value); } public final void setLongVolatile(Object obj, long value) { checkKind(StaticPropertyKind.Long); - UNSAFE.putLongVolatile(obj, offset, value); + UNSAFE.putLongVolatile(shape.getStorage(obj, true), offset, value); } public final boolean compareAndSwapLong(Object obj, long before, long after) { checkKind(StaticPropertyKind.Long); - return UNSAFE.compareAndSwapLong(obj, offset, before, after); + return UNSAFE.compareAndSwapLong(shape.getStorage(obj, true), offset, before, after); } public final long getAndAddLong(Object obj, long value) { checkKind(StaticPropertyKind.Long); - return UNSAFE.getAndAddLong(obj, offset, value); + return UNSAFE.getAndAddLong(shape.getStorage(obj, true), offset, value); } public final long getAndSetLong(Object obj, long value) { checkKind(StaticPropertyKind.Long); - return UNSAFE.getAndSetLong(obj, offset, value); + return UNSAFE.getAndSetLong(shape.getStorage(obj, true), offset, value); } // short field access public final short getShort(Object obj) { checkKind(StaticPropertyKind.Short); - return UNSAFE.getShort(obj, (long) offset); + return UNSAFE.getShort(shape.getStorage(obj, true), (long) offset); } public final short getShortVolatile(Object obj) { checkKind(StaticPropertyKind.Short); - return UNSAFE.getShortVolatile(obj, offset); + return UNSAFE.getShortVolatile(shape.getStorage(obj, true), offset); } public final void setShort(Object obj, short value) { checkKind(StaticPropertyKind.Short); - UNSAFE.putShort(obj, (long) offset, value); + UNSAFE.putShort(shape.getStorage(obj, true), (long) offset, value); } public final void setShortVolatile(Object obj, short value) { checkKind(StaticPropertyKind.Short); - UNSAFE.putShortVolatile(obj, offset, value); + UNSAFE.putShortVolatile(shape.getStorage(obj, true), offset, value); } private static Unsafe getUnsafe() { diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticPropertyKind.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticPropertyKind.java index 820fb80813a5..ce70a19dc9ed 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticPropertyKind.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticPropertyKind.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2021, 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 @@ -23,6 +23,9 @@ package com.oracle.truffle.espresso.staticobject; public enum StaticPropertyKind { + // The ordinal values of these enum types influences field scheduling in ArrayBasedStaticShape. + // There, we want to schedule 'bigger' field types first (see `getBitCount()`). + /** * The primitive long kind. */ diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java new file mode 100644 index 000000000000..51d156bc2d06 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +public abstract class StaticShape { + protected final Class storageClass; + protected final T factory; + + StaticShape(Class storageClass, T factory) { + this.storageClass = storageClass; + this.factory = factory; + } + + public static StaticShapeBuilder newBuilder() { + return new StaticShapeBuilder(); + } + + public final T getFactory() { + return factory; + } + + public final Class getStorageClass() { + return storageClass; + } + + abstract Object getStorage(Object obj, boolean primitive); + + static Object cast(Object obj, Class type) { + return type.cast(obj); + } + + @SuppressWarnings("unchecked") + final Class getFactoryInterface() { + // Builder.validate() makes sure that the factory class implements a single interface + assert factory.getClass().getInterfaces().length == 1; + return (Class) factory.getClass().getInterfaces()[0]; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java new file mode 100644 index 000000000000..1500f2ee3fc4 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Objects; + +public final class StaticShapeBuilder { + private final HashMap extendedProperties = new HashMap<>(); + + StaticShapeBuilder() { + } + + public StaticShapeBuilder property(StaticProperty property, String name, boolean isFinal) { + Objects.requireNonNull(property); + if (extendedProperties.containsKey(name)) { + throw new IllegalArgumentException("This builder already contains a property named '" + name + "'"); + } + extendedProperties.put(name, new ExtendedProperty(property, name, isFinal)); + return this; + } + + public StaticShape build() { + // The classloader that loaded the default superClass must be able to load the default + // factory. + // Therefore, we can't use java.lang.Object as default superClass. + return build(DefaultStaticObject.class, DefaultStaticObject.DefaultStaticObjectFactory.class); + } + + public StaticShape build(StaticShape parentShape) { + Objects.requireNonNull(parentShape); + ShapeGenerator sg = ShapeGenerator.getShapeGenerator(parentShape, extendedProperties.values()); + return build(sg); + + } + + public StaticShape build(Class superClass, Class factoryInterface) { + validate(factoryInterface, superClass); + ShapeGenerator sg = ShapeGenerator.getShapeGenerator(superClass, factoryInterface, extendedProperties.values()); + return build(sg); + } + + private StaticShape build(ShapeGenerator sg) { + StaticShape shape = sg.generateShape(); + for (ExtendedProperty extendedProperty : extendedProperties.values()) { + extendedProperty.property.initShape(shape); + } + return shape; + } + + private static void validate(Class storageFactoryInterface, Class storageSuperClass) { + if (!storageFactoryInterface.isInterface()) { + throw new RuntimeException(storageFactoryInterface.getName() + " must be an interface."); + } + for (Method m : storageFactoryInterface.getMethods()) { + if (!m.getReturnType().isAssignableFrom(storageSuperClass)) { + throw new RuntimeException("The return type of '" + m.getReturnType().getName() + " " + storageFactoryInterface.getName() + "." + m.toString() + "' is not assignable from '" + + storageSuperClass.getName() + "'"); + } + try { + storageSuperClass.getDeclaredConstructor(m.getParameterTypes()); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Method '" + m.toString() + "' does not match any constructor in '" + storageSuperClass.getName() + "'", e); + } + } + } + + static final class ExtendedProperty { + private final StaticProperty property; + private final String name; + private final boolean isFinal; + + ExtendedProperty(StaticProperty property, String name, boolean isFinal) { + this.property = property; + this.name = name; + this.isFinal = isFinal; + } + + StaticProperty getProperty() { + return property; + } + + String getName() { + return name; + } + + boolean isFinal() { + return isFinal; + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java index 80c93d8efa51..62f117d88bee 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java @@ -327,7 +327,7 @@ private ObjectKlass createAndPutKlass(Meta meta, ParserKlass parserKlass, Symbol try (DebugCloseable define = KLASS_DEFINE.scope(getContext().getTimers())) { // FIXME(peterssen): Do NOT create a LinkedKlass every time, use a global cache. - LinkedKlass linkedKlass = new LinkedKlass(parserKlass, superKlass == null ? null : superKlass.getLinkedKlass(), linkedInterfaces); + LinkedKlass linkedKlass = LinkedKlass.create(parserKlass, superKlass == null ? null : superKlass.getLinkedKlass(), linkedInterfaces); klass = new ObjectKlass(context, linkedKlass, superKlass, superInterfaces, getClassLoader()); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java index 737cf3c7d575..defca28b5301 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java @@ -286,9 +286,9 @@ private Object getObjectHelper(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getObjectVolatile(obj.getObjectFieldStorage()); + return linkedField.getObjectVolatile(obj); } else { - return linkedField.getObject(obj.getObjectFieldStorage()); + return linkedField.getObject(obj); } } @@ -296,9 +296,9 @@ private void setObjectHelper(StaticObject obj, Object value, boolean forceVolati obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setObjectVolatile(obj.getObjectFieldStorage(), value); + linkedField.setObjectVolatile(obj, value); } else { - linkedField.setObject(obj.getObjectFieldStorage(), value); + linkedField.setObject(obj, value); } } // endregion helper methods @@ -326,14 +326,14 @@ public StaticObject getAndSetObject(StaticObject obj, StaticObject value) { obj.checkNotForeign(); assert !isHidden() : this + " is hidden"; assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); - return (StaticObject) linkedField.getAndSetObject(obj.getObjectFieldStorage(), value); + return (StaticObject) linkedField.getAndSetObject(obj, value); } public boolean compareAndSwapObject(StaticObject obj, Object before, Object after) { obj.checkNotForeign(); assert !isHidden() : this + " is hidden"; assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); - return linkedField.compareAndSwapObject(obj.getObjectFieldStorage(), before, after); + return linkedField.compareAndSwapObject(obj, before, after); } // region hidden Object @@ -366,9 +366,9 @@ public boolean getBoolean(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getBooleanVolatile(obj.getPrimitiveFieldStorage()); + return linkedField.getBooleanVolatile(obj); } else { - return linkedField.getBoolean(obj.getPrimitiveFieldStorage()); + return linkedField.getBoolean(obj); } } @@ -380,9 +380,9 @@ public void setBoolean(StaticObject obj, boolean value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setBooleanVolatile(obj.getPrimitiveFieldStorage(), value); + linkedField.setBooleanVolatile(obj, value); } else { - linkedField.setBoolean(obj.getPrimitiveFieldStorage(), value); + linkedField.setBoolean(obj, value); } } // endregion boolean @@ -396,9 +396,9 @@ public byte getByte(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getByteVolatile(obj.getPrimitiveFieldStorage()); + return linkedField.getByteVolatile(obj); } else { - return linkedField.getByte(obj.getPrimitiveFieldStorage()); + return linkedField.getByte(obj); } } @@ -410,9 +410,9 @@ public void setByte(StaticObject obj, byte value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setByteVolatile(obj.getPrimitiveFieldStorage(), value); + linkedField.setByteVolatile(obj, value); } else { - linkedField.setByte(obj.getPrimitiveFieldStorage(), value); + linkedField.setByte(obj, value); } } // endregion byte @@ -426,9 +426,9 @@ public char getChar(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getCharVolatile(obj.getPrimitiveFieldStorage()); + return linkedField.getCharVolatile(obj); } else { - return linkedField.getChar(obj.getPrimitiveFieldStorage()); + return linkedField.getChar(obj); } } @@ -440,9 +440,9 @@ public void setChar(StaticObject obj, char value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setCharVolatile(obj.getPrimitiveFieldStorage(), value); + linkedField.setCharVolatile(obj, value); } else { - linkedField.setChar(obj.getPrimitiveFieldStorage(), value); + linkedField.setChar(obj, value); } } // endregion char @@ -456,9 +456,9 @@ public double getDouble(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getDoubleVolatile(obj.getPrimitiveFieldStorage()); + return linkedField.getDoubleVolatile(obj); } else { - return linkedField.getDouble(obj.getPrimitiveFieldStorage()); + return linkedField.getDouble(obj); } } @@ -470,9 +470,9 @@ public void setDouble(StaticObject obj, double value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setDoubleVolatile(obj.getPrimitiveFieldStorage(), value); + linkedField.setDoubleVolatile(obj, value); } else { - linkedField.setDouble(obj.getPrimitiveFieldStorage(), value); + linkedField.setDouble(obj, value); } } @@ -487,9 +487,9 @@ public float getFloat(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getFloatVolatile(obj.getPrimitiveFieldStorage()); + return linkedField.getFloatVolatile(obj); } else { - return linkedField.getFloat(obj.getPrimitiveFieldStorage()); + return linkedField.getFloat(obj); } } @@ -501,9 +501,9 @@ public void setFloat(StaticObject obj, float value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setFloatVolatile(obj.getPrimitiveFieldStorage(), value); + linkedField.setFloatVolatile(obj, value); } else { - linkedField.setFloat(obj.getPrimitiveFieldStorage(), value); + linkedField.setFloat(obj, value); } } // endregion float @@ -517,9 +517,9 @@ public int getInt(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getIntVolatile(obj.getPrimitiveFieldStorage()); + return linkedField.getIntVolatile(obj); } else { - return linkedField.getInt(obj.getPrimitiveFieldStorage()); + return linkedField.getInt(obj); } } @@ -531,16 +531,16 @@ public void setInt(StaticObject obj, int value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setIntVolatile(obj.getPrimitiveFieldStorage(), value); + linkedField.setIntVolatile(obj, value); } else { - linkedField.setInt(obj.getPrimitiveFieldStorage(), value); + linkedField.setInt(obj, value); } } public boolean compareAndSwapInt(StaticObject obj, int before, int after) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); - return linkedField.compareAndSwapInt(obj.getPrimitiveFieldStorage(), before, after); + return linkedField.compareAndSwapInt(obj, before, after); } // endregion int @@ -554,9 +554,9 @@ public long getLong(StaticObject obj, boolean forceVolatile) { assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); assert getKind().needsTwoSlots(); if (isVolatile() || forceVolatile) { - return linkedField.getLongVolatile(obj.getPrimitiveFieldStorage()); + return linkedField.getLongVolatile(obj); } else { - return linkedField.getLong(obj.getPrimitiveFieldStorage()); + return linkedField.getLong(obj); } } @@ -569,9 +569,9 @@ public void setLong(StaticObject obj, long value, boolean forceVolatile) { assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); assert getKind().needsTwoSlots(); if (isVolatile() || forceVolatile) { - linkedField.setLongVolatile(obj.getPrimitiveFieldStorage(), value); + linkedField.setLongVolatile(obj, value); } else { - linkedField.setLong(obj.getPrimitiveFieldStorage(), value); + linkedField.setLong(obj, value); } } @@ -579,7 +579,7 @@ public boolean compareAndSwapLong(StaticObject obj, long before, long after) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); assert getKind().needsTwoSlots(); - return linkedField.compareAndSwapLong(obj.getPrimitiveFieldStorage(), before, after); + return linkedField.compareAndSwapLong(obj, before, after); } // endregion long @@ -592,9 +592,9 @@ public short getShort(StaticObject obj, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - return linkedField.getShortVolatile(obj.getPrimitiveFieldStorage()); + return linkedField.getShortVolatile(obj); } else { - return linkedField.getShort(obj.getPrimitiveFieldStorage()); + return linkedField.getShort(obj); } } @@ -606,9 +606,9 @@ public void setShort(StaticObject obj, short value, boolean forceVolatile) { obj.checkNotForeign(); assert getDeclaringKlass().isAssignableFrom(obj.getKlass()) : this + " does not exist in " + obj.getKlass(); if (isVolatile() || forceVolatile) { - linkedField.setShortVolatile(obj.getPrimitiveFieldStorage(), value); + linkedField.setShortVolatile(obj, value); } else { - linkedField.setShort(obj.getPrimitiveFieldStorage(), value); + linkedField.setShort(obj, value); } } // endregion short diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java index 99ce4a616b40..4294d1f388a3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java @@ -33,14 +33,14 @@ final class LinkedField extends StaticProperty { private final ParserField parserField; private final int slot; - LinkedField(ParserField parserField, int slot, int offset) { - super(parserField.getPropertyKind(), offset); + LinkedField(ParserField parserField, int slot) { + super(parserField.getPropertyKind()); this.parserField = parserField; this.slot = slot; } - public static LinkedField createHidden(Symbol name, int slot, int offset) { - return new LinkedField(new ParserField(ParserField.HIDDEN, name, Type.java_lang_Object, null), slot, offset); + public static LinkedField createHidden(Symbol name, int slot) { + return new LinkedField(new ParserField(ParserField.HIDDEN, name, Type.java_lang_Object, null), slot); } /** diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java index 4e5d89d23f62..b2dd7e1c1e66 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java @@ -26,12 +26,15 @@ import java.lang.reflect.Modifier; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.espresso.classfile.ConstantPool; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Name; import com.oracle.truffle.espresso.descriptors.Symbol.Type; import com.oracle.truffle.espresso.runtime.Attribute; +import com.oracle.truffle.espresso.runtime.StaticObject.StaticObjectFactory; +import com.oracle.truffle.espresso.staticobject.StaticShape; // Structural shareable klass (superklass in superinterfaces resolved and linked) // contains shape, field locations. @@ -52,12 +55,28 @@ public final class LinkedKlass { private final boolean hasFinalizer; - private final LinkedKlassFieldLayout fieldLayout; + private final StaticShape instanceShape; + private final StaticShape staticShape; - public LinkedKlass(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces) { + // instance fields declared in the corresponding LinkedKlass (includes hidden fields) + @CompilerDirectives.CompilationFinal(dimensions = 1) // + final LinkedField[] instanceFields; + // static fields declared in the corresponding LinkedKlass (no hidden fields) + @CompilerDirectives.CompilationFinal(dimensions = 1) // + final LinkedField[] staticFields; + + final int fieldTableLength; + + private LinkedKlass(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces, StaticShape instanceShape, + StaticShape staticShape, LinkedField[] instanceFields, LinkedField[] staticFields, int fieldTableLength) { this.parserKlass = parserKlass; this.superKlass = superKlass; this.interfaces = interfaces; + this.instanceShape = instanceShape; + this.staticShape = staticShape; + this.instanceFields = instanceFields; + this.staticFields = staticFields; + this.fieldTableLength = fieldTableLength; // Streams are forbidden in Espresso. // assert Arrays.stream(interfaces).allMatch(i -> Modifier.isInterface(i.getFlags())); @@ -77,34 +96,39 @@ public LinkedKlass(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[ // supported. linkedMethods[i] = new LinkedMethod(parserMethod); } - this.methods = linkedMethods; - - fieldLayout = LinkedKlassFieldLayout.create(parserKlass, superKlass); - } - - public int getObjectFieldsCount() { - return fieldLayout.objectFields; - } - - public int getInstancePrimitiveToAlloc() { - return fieldLayout.instanceToAlloc; - } - - public int getPrimitiveInstanceFieldLastOffset() { - return fieldLayout.primInstanceLastOffset; - } - - public int getStaticObjectFieldsCount() { - return fieldLayout.staticObjectFields; - } - - public int getStaticPrimitiveToAlloc() { - return fieldLayout.staticToAlloc; } - public int getPrimitiveStaticFieldLastOffset() { - return fieldLayout.primStaticLastOffset; + public static LinkedKlass create(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces) { + LinkedKlassFieldLayout fieldLayout = new LinkedKlassFieldLayout(parserKlass, superKlass); + return new LinkedKlass( + parserKlass, + superKlass, + interfaces, + fieldLayout.instanceShape, + fieldLayout.staticShape, + fieldLayout.instanceFields, + fieldLayout.staticFields, + fieldLayout.fieldTableLength); + } + + public static LinkedKlass redefine(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces, LinkedKlass redefinedKlass) { + // On class redefinition we need to re-use the old shape. + // If we don't do it, shape checks on field accesses fail because `Field` instances in + // `ObjectKlass.fieldTable` hold references to the old shape, which does not match the shape + // of the new object instances. + // If we work around this issue by patching the `ObjectKlass.fieldTable` on class + // redefinition, these new `Field` instances cannot be used to access object instances with + // the old shape. + return new LinkedKlass( + parserKlass, + superKlass, + interfaces, + redefinedKlass.instanceShape, + redefinedKlass.staticShape, + redefinedKlass.instanceFields, + redefinedKlass.staticFields, + redefinedKlass.fieldTableLength); } int getFlags() { @@ -156,18 +180,18 @@ LinkedMethod[] getLinkedMethods() { } LinkedField[] getInstanceFields() { - return fieldLayout.instanceFields; + return instanceFields; } LinkedField[] getStaticFields() { - return fieldLayout.staticFields; + return staticFields; } int getFieldTableLength() { - return fieldLayout.fieldTableLength; + return fieldTableLength; } - int[][] getLeftoverHoles() { - return fieldLayout.leftoverHoles; + public StaticShape getShape(boolean isStatic) { + return isStatic ? staticShape : instanceShape; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java index b228edb35cc7..3aab7a9dd5eb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -22,262 +22,68 @@ */ package com.oracle.truffle.espresso.impl; -import java.util.ArrayList; -import java.util.List; - -import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Name; import com.oracle.truffle.espresso.descriptors.Symbol.Type; -import com.oracle.truffle.espresso.meta.EspressoError; -import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.runtime.StaticObject; -import sun.misc.Unsafe; +import com.oracle.truffle.espresso.runtime.StaticObject.StaticObjectFactory; +import com.oracle.truffle.espresso.staticobject.StaticShape; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; final class LinkedKlassFieldLayout { - /* - * If the object model does not start on a long-aligned offset. To manage, we will align our - * indexes to the actual relative address to the start of the object. Note that we still make a - * pretty strong assumption here: All arrays are allocated at an address aligned with a *long* - * - * Note that we cannot use static final fields here, as SVM initializes them at build time using - * HotSpot's values, and thus the values for base and alignment would not be correct. - */ - - private static int base() { - return Unsafe.ARRAY_BYTE_BASE_OFFSET; - } - - private static int alignmentCorrection() { - int misalignment = Unsafe.ARRAY_BYTE_BASE_OFFSET % Unsafe.ARRAY_LONG_INDEX_SCALE; - return misalignment == 0 ? 0 : Unsafe.ARRAY_LONG_INDEX_SCALE - misalignment; - } - - private static final int N_PRIMITIVES = 8; - private static final JavaKind[] order = {JavaKind.Long, JavaKind.Double, JavaKind.Int, JavaKind.Float, JavaKind.Short, JavaKind.Char, JavaKind.Byte, JavaKind.Boolean}; + final StaticShape instanceShape; + final StaticShape staticShape; // instance fields declared in the corresponding LinkedKlass (includes hidden fields) - @CompilerDirectives.CompilationFinal(dimensions = 1) // + @CompilationFinal(dimensions = 1) // final LinkedField[] instanceFields; // static fields declared in the corresponding LinkedKlass (no hidden fields) - @CompilerDirectives.CompilationFinal(dimensions = 1) // + @CompilationFinal(dimensions = 1) // final LinkedField[] staticFields; - @CompilerDirectives.CompilationFinal(dimensions = 2) // - final int[][] leftoverHoles; - final int instanceToAlloc; - final int staticToAlloc; - final int primInstanceLastOffset; - final int primStaticLastOffset; final int fieldTableLength; - final int objectFields; - final int staticObjectFields; - private LinkedKlassFieldLayout(LinkedField[] instanceFields, LinkedField[] staticFields, int[][] leftoverHoles, - int instanceToAlloc, int staticToAlloc, - int primInstanceLastOffset, int primStaticLastOffset, - int fieldTableLength, - int objectFields, int staticObjectFields) { - this.instanceFields = instanceFields; - this.staticFields = staticFields; - this.leftoverHoles = leftoverHoles; - this.instanceToAlloc = instanceToAlloc; - this.staticToAlloc = staticToAlloc; - this.primInstanceLastOffset = primInstanceLastOffset; - this.primStaticLastOffset = primStaticLastOffset; - this.fieldTableLength = fieldTableLength; - this.objectFields = objectFields; - this.staticObjectFields = staticObjectFields; - } + LinkedKlassFieldLayout(ParserKlass parserKlass, LinkedKlass superKlass) { + StaticShapeBuilder instanceBuilder = StaticShape.newBuilder(); + StaticShapeBuilder staticBuilder = StaticShape.newBuilder(); - /** - *

    - * Creates a layout for the primitive fields of a given class, and assigns to each field the raw - * offset in the byte array that represents the data. The layout tries to be as compact as - * possible. The rules for determining the layout are as follow: - * - *

  • The Top klass (j.l.Object) will have its field offset corresponding the point where the - * data in the byte array begins (the first offset after the array header)
  • - *
  • If this offset is not long-aligned, then start further so that this new offset is aligned - * to a long. Register that there is some space between the start of the raw data and the first - * field offset (perhaps a byte could be squeezed in).
  • - *
  • Other klasses will inherit their super klass' layout, and start appending their own - * declared field at the first offset aligned with the biggest primitive a given class has.
  • - *
  • If there are known holes in the parents layout, the klass will attempt to squeeze its own - * fields in these holes.
  • - *

    - *

    - * For example, suppose we have the following hierarchy, and that the first offset of the data - * in a byte array is at 14: - *

    - * - *
    -     * class A {
    -     *     long l;
    -     *     int i;
    -     * }
    -     * 
    -     * class B extends A {
    -     *     double d;
    -     * }
    -     * 
    -     * class C extends B {
    -     *     float f;
    -     *     short s;
    -     * }
    -     * 
    - * - * Then, the resulting layout for A would be: - * - *
    -     * - 0-13: header
    -     * - 14-15: unused  -> Padding for aligned long
    -     * - 16-23: l
    -     * - 24-27: i
    -     * 
    - * - * the resulting layout for B would be: - * - *
    -     * - 0-13: header   }
    -     * - 14-15: unused  }   Same as
    -     * - 16-23: l       }      A
    -     * - 24-27: i       }
    -     * - 28-31: unused  -> Padding for aligned double
    -     * - 32-39: d
    -     * 
    - * - * the resulting layout for C would be: - * - *
    -     * - 0-13: header   
    -     * - 14-15: s       ->   hole filled
    -     * - 16-23: l       
    -     * - 24-27: i       
    -     * - 28-31: f       ->   hole filled
    -     * - 32-39: d
    -     * 
    - */ - static LinkedKlassFieldLayout create(ParserKlass parserKlass, LinkedKlass superKlass) { FieldCounter fieldCounter = new FieldCounter(parserKlass); - - // Stats about primitive fields - int superTotalInstanceByteCount; - int superTotalStaticByteCount; - int[][] leftoverHoles; - - // Indexes for object fields - int instanceFieldInsertionIndex = 0; - int nextFieldTableSlot; - int nextObjectFieldIndex; - // The staticFieldTable does not include fields of parent classes. - // Therefore, nextStaticFieldTableSlot can be used also as staticFieldInsertionIndex. - int nextStaticFieldTableSlot = 0; - int nextStaticObjectFieldIndex; - - if (superKlass != null) { - superTotalInstanceByteCount = superKlass.getPrimitiveInstanceFieldLastOffset(); - superTotalStaticByteCount = superKlass.getPrimitiveStaticFieldLastOffset(); - leftoverHoles = superKlass.getLeftoverHoles(); - nextFieldTableSlot = superKlass.getFieldTableLength(); - nextObjectFieldIndex = superKlass.getObjectFieldsCount(); - nextStaticObjectFieldIndex = superKlass.getStaticObjectFieldsCount(); - } else { - // Align the starting offset to a long. - superTotalInstanceByteCount = base() + alignmentCorrection(); - superTotalStaticByteCount = base() + alignmentCorrection(); - // Register a hole if we had to realign. - if (alignmentCorrection() > 0) { - leftoverHoles = new int[][]{{base(), base() + alignmentCorrection()}}; - } else { - leftoverHoles = new int[0][]; - } - nextFieldTableSlot = 0; - nextObjectFieldIndex = 0; - nextStaticObjectFieldIndex = 0; - } - - PrimitiveFieldIndexes instancePrimitiveFieldIndexes = new PrimitiveFieldIndexes(fieldCounter.instancePrimitiveFields, superTotalInstanceByteCount, leftoverHoles); - PrimitiveFieldIndexes staticPrimitiveFieldIndexes = new PrimitiveFieldIndexes(fieldCounter.staticPrimitiveFields, superTotalStaticByteCount, FillingSchedule.EMPTY_INT_ARRAY_ARRAY); - - LinkedField[] instanceFields = new LinkedField[fieldCounter.instanceFields]; - LinkedField[] staticFields = new LinkedField[fieldCounter.staticFields]; + int nextInstanceFieldIndex = 0; + int nextStaticFieldIndex = 0; + int nextInstanceFieldSlot = superKlass == null ? 0 : superKlass.getFieldTableLength(); + int nextStaticFieldSlot = 0; + instanceFields = new LinkedField[fieldCounter.instanceFields]; + staticFields = new LinkedField[fieldCounter.staticFields]; for (ParserField parserField : parserKlass.getFields()) { - JavaKind kind = parserField.getKind(); - int offset; if (parserField.isStatic()) { - if (kind.isPrimitive()) { - offset = staticPrimitiveFieldIndexes.getOffset(kind); - } else { - offset = StaticObject.getObjectArrayOffset(nextStaticObjectFieldIndex++); - } - LinkedField linkedField = new LinkedField(parserField, nextStaticFieldTableSlot, offset); - staticFields[nextStaticFieldTableSlot++] = linkedField; + LinkedField field = new LinkedField(parserField, nextStaticFieldSlot++); + staticBuilder.property(field, parserField.getName().toString(), parserField.isFinal()); + staticFields[nextStaticFieldIndex++] = field; } else { - if (kind.isPrimitive()) { - offset = instancePrimitiveFieldIndexes.getOffset(kind); - } else { - offset = StaticObject.getObjectArrayOffset(nextObjectFieldIndex++); - } - LinkedField linkedField = new LinkedField(parserField, nextFieldTableSlot++, offset); - instanceFields[instanceFieldInsertionIndex++] = linkedField; + LinkedField field = new LinkedField(parserField, nextInstanceFieldSlot++); + instanceBuilder.property(field, parserField.getName().toString(), parserField.isFinal()); + instanceFields[nextInstanceFieldIndex++] = field; } } - - // Add hidden fields after all instance fields for (Symbol hiddenFieldName : fieldCounter.hiddenFieldNames) { - int offset = StaticObject.getObjectArrayOffset(nextObjectFieldIndex++); - LinkedField hiddenField = LinkedField.createHidden(hiddenFieldName, nextFieldTableSlot++, offset); - instanceFields[instanceFieldInsertionIndex++] = hiddenField; - } - - int instancePrimToAlloc = getSizeToAlloc(superKlass == null ? 0 : superKlass.getInstancePrimitiveToAlloc(), instancePrimitiveFieldIndexes); - int staticPrimToAlloc = getSizeToAlloc(superKlass == null ? 0 : superKlass.getStaticPrimitiveToAlloc(), staticPrimitiveFieldIndexes); - - return new LinkedKlassFieldLayout( - instanceFields, staticFields, instancePrimitiveFieldIndexes.schedule.nextLeftoverHoles, - instancePrimToAlloc, staticPrimToAlloc, - instancePrimitiveFieldIndexes.offsets[N_PRIMITIVES - 1], staticPrimitiveFieldIndexes.offsets[N_PRIMITIVES - 1], - nextFieldTableSlot, - nextObjectFieldIndex, nextStaticObjectFieldIndex); - } - - private static int getSizeToAlloc(int superToAlloc, PrimitiveFieldIndexes fieldIndexes) { - int toAlloc = fieldIndexes.offsets[N_PRIMITIVES - 1] - base(); - assert toAlloc >= 0; - if (toAlloc == alignmentCorrection() && fieldIndexes.schedule.isEmpty()) { - // If superKlass has fields in the alignment hole, we will need to allocate. If not, we - // can save an array. Note that if such a field exists, we will allocate an array of - // size at least the alignment correction, since we fill holes from the right to the - // left. - toAlloc = superToAlloc; + ParserField hiddenParserField = new ParserField(ParserField.HIDDEN, hiddenFieldName, Type.java_lang_Object, null); + LinkedField field = new LinkedField(hiddenParserField, nextInstanceFieldSlot++); + instanceBuilder.property(field, hiddenFieldName.toString(), false); + instanceFields[nextInstanceFieldIndex++] = field; } - return toAlloc; - } - - private static int indexFromKind(JavaKind kind) { - // @formatter:off - switch (kind) { - case Boolean: return 7; - case Byte : return 6; - case Short : return 4; - case Char : return 5; - case Int : return 2; - case Float : return 3; - case Long : return 0; - case Double : return 1; - default: - throw EspressoError.shouldNotReachHere(); + if (superKlass == null) { + instanceShape = instanceBuilder.build(StaticObject.class, StaticObjectFactory.class); + } else { + instanceShape = instanceBuilder.build(superKlass.getShape(false)); } - // @formatter:on + staticShape = staticBuilder.build(StaticObject.class, StaticObjectFactory.class); + fieldTableLength = nextInstanceFieldSlot; } private static final class FieldCounter { - final int[] instancePrimitiveFields = new int[N_PRIMITIVES]; - final int[] staticPrimitiveFields = new int[N_PRIMITIVES]; - final Symbol[] hiddenFieldNames; // Includes hidden fields @@ -288,17 +94,10 @@ private static final class FieldCounter { int iFields = 0; int sFields = 0; for (ParserField f : parserKlass.getFields()) { - JavaKind kind = f.getKind(); if (f.isStatic()) { sFields++; - if (kind.isPrimitive()) { - staticPrimitiveFields[indexFromKind(kind)]++; - } } else { iFields++; - if (kind.isPrimitive()) { - instancePrimitiveFields[indexFromKind(kind)]++; - } } } // All hidden fields are of Object kind @@ -373,172 +172,4 @@ private static Symbol[] getHiddenFieldNames(ParserKlass parserKlass) { return Symbol.EMPTY_ARRAY; } } - - private static final class PrimitiveFieldIndexes { - final int[] offsets; - final FillingSchedule schedule; - - // To ignore leftoverHoles, pass FillingSchedule.EMPTY_INT_ARRAY_ARRAY. - // This is used for static fields, where the gain would be negligible. - PrimitiveFieldIndexes(int[] primitiveFields, int superTotalByteCount, int[][] leftoverHoles) { - offsets = new int[N_PRIMITIVES]; - offsets[0] = startOffset(superTotalByteCount, primitiveFields); - this.schedule = FillingSchedule.create(superTotalByteCount, offsets[0], primitiveFields, leftoverHoles); - // FillingSchedule.create() modifies primitiveFields. - // Only offsets[0] must be initialized before creating the filling schedule. - for (int i = 1; i < N_PRIMITIVES; i++) { - offsets[i] = offsets[i - 1] + primitiveFields[i - 1] * order[i - 1].getByteCount(); - } - } - - int getOffset(JavaKind kind) { - ScheduleEntry entry = schedule.query(kind); - if (entry != null) { - return entry.offset; - } else { - int offsetsIndex = indexFromKind(kind); - int prevOffset = offsets[offsetsIndex]; - offsets[offsetsIndex] += kind.getByteCount(); - return prevOffset; - } - } - - // Find first primitive to set, and align on it. - private static int startOffset(int superTotalByteCount, int[] primitiveCounts) { - int i = 0; - while (i < N_PRIMITIVES && primitiveCounts[i] == 0) { - i++; - } - if (i == N_PRIMITIVES) { - return superTotalByteCount; - } - int r = superTotalByteCount % order[i].getByteCount(); - if (r == 0) { - return superTotalByteCount; - } - return superTotalByteCount + order[i].getByteCount() - r; - } - } - - /** - * Greedily tries to fill the space between a parent's fields and its child. - */ - private static final class FillingSchedule { - static final int[][] EMPTY_INT_ARRAY_ARRAY = new int[0][]; - - final List schedule; - int[][] nextLeftoverHoles; - - final boolean isEmpty; - - boolean isEmpty() { - return isEmpty; - } - - static FillingSchedule create(int holeStart, int holeEnd, int[] counts, int[][] leftoverHoles) { - List schedule = new ArrayList<>(); - if (leftoverHoles == EMPTY_INT_ARRAY_ARRAY) { - // packing static fields is not as interesting as instance fields: the array created - // to remember the hole would be bigger than what we would gain. Only schedule for - // direct parent. - scheduleHole(holeStart, holeEnd, counts, schedule); - return new FillingSchedule(schedule); - } else { - List nextHoles = new ArrayList<>(); - scheduleHole(holeStart, holeEnd, counts, schedule, nextHoles); - if (leftoverHoles != null) { - for (int[] hole : leftoverHoles) { - scheduleHole(hole[0], hole[1], counts, schedule, nextHoles); - } - } - return new FillingSchedule(schedule, nextHoles); - } - } - - private static void scheduleHole(int holeStart, int holeEnd, int[] counts, List schedule, List nextHoles) { - int end = holeEnd; - int holeSize = holeEnd - holeStart; - int i = 0; - - mainloop: while (holeSize > 0 && i < N_PRIMITIVES) { - int byteCount = order[i].getByteCount(); - while (counts[i] > 0 && byteCount <= holeSize) { - int newEnd = end - byteCount; - if (newEnd % byteCount != 0) { - int misalignment = newEnd % byteCount; - int aligned = newEnd - misalignment; - if (aligned < holeStart) { - // re-aligning the store makes it overlap with somethig else: abort. - i++; - continue mainloop; - } - schedule.add(new ScheduleEntry(order[i], aligned)); - counts[i]--; - // We created a new hole of size `misaligned`. Try to fill it. - scheduleHole(end - misalignment, end, counts, schedule, nextHoles); - newEnd = aligned; - } else { - counts[i]--; - schedule.add(new ScheduleEntry(order[i], newEnd)); - } - end = newEnd; - holeSize = end - holeStart; - } - i++; - } - if (holeSize > 0) { - nextHoles.add(new int[]{holeStart, end}); - } - } - - private static void scheduleHole(int holeStart, int holeEnd, int[] counts, List schedule) { - int end = holeEnd; - int holeSize = holeEnd - holeStart; - int i = 0; - - while (holeSize > 0 && i < N_PRIMITIVES) { - if (counts[i] > 0 && order[i].getByteCount() <= holeSize) { - while (counts[i] > 0 && order[i].getByteCount() <= holeSize) { - counts[i]--; - end -= order[i].getByteCount(); - holeSize -= order[i].getByteCount(); - schedule.add(new ScheduleEntry(order[i], end)); - } - } - i++; - } - assert holeSize >= 0; - } - - private FillingSchedule(List schedule) { - this.schedule = schedule; - this.isEmpty = schedule == null || schedule.isEmpty(); - } - - private FillingSchedule(List schedule, List nextHoles) { - this.schedule = schedule; - this.nextLeftoverHoles = nextHoles.isEmpty() ? null : nextHoles.toArray(EMPTY_INT_ARRAY_ARRAY); - this.isEmpty = schedule != null && schedule.isEmpty(); - } - - ScheduleEntry query(JavaKind kind) { - for (ScheduleEntry e : schedule) { - if (e.kind == kind) { - schedule.remove(e); - return e; - } - } - return null; - } - } - - private static class ScheduleEntry { - final JavaKind kind; - final int offset; - - ScheduleEntry(JavaKind kind, int offset) { - this.kind = kind; - this.offset = offset; - } - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index 3ab777f3ddd5..ff07c2f97db8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1099,7 +1099,7 @@ public void redefineClass(ChangePacket packet, List refreshSubClass for (int i = 0; i < superInterfaces.length; i++) { interfaces[i] = superInterfaces[i].getLinkedKlass(); } - LinkedKlass linkedKlass = new LinkedKlass(parserKlass, getSuperKlass().getLinkedKlass(), interfaces); + LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); // fields if (!change.getOuterFields().isEmpty()) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserField.java index 6b54f6b9190b..1e172d3eae2f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserField.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ParserField.java @@ -81,6 +81,10 @@ public boolean isStatic() { return Modifier.isStatic(flags); } + public boolean isFinal() { + return Modifier.isFinal(flags); + } + public JavaKind getKind() { return Types.getJavaKind(type); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 72e26bf56c9d..f27f7339e065 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -60,29 +60,19 @@ * instances of {@link StaticObject}. */ @ExportLibrary(DynamicDispatchLibrary.class) -public final class StaticObject implements TruffleObject { +public class StaticObject implements TruffleObject, Cloneable { public static final StaticObject[] EMPTY_ARRAY = new StaticObject[0]; public static final StaticObject NULL = new StaticObject(); public static final String CLASS_TO_STATIC = "static"; private static final Unsafe UNSAFE = UnsafeAccess.get(); - private static final byte[] FOREIGN_OBJECT_MARKER = new byte[0]; private final Klass klass; // != PrimitiveKlass - /** - * Stores non-primitive fields only. - */ - private final Object fields; + private final Object arrayOrForeignObject; - /** - * Stores all primitive types contiguously in a single byte array, without any unused bits - * between prims (except for 7 bits with booleans). In order to quickly reconstruct a long (for - * example), which would require reading 8 bytes and concatenating them, call Unsafe which can - * directly read a long. - */ - private final byte[] primitiveFields; + private final boolean isForeign; private volatile EspressoLock lock; @@ -98,57 +88,33 @@ public final class StaticObject implements TruffleObject { private StaticObject() { assert NULL == null : "Only meant for StaticObject.NULL"; this.klass = null; - this.fields = null; - this.primitiveFields = null; - } - - // Constructor for object copy. - private StaticObject(ObjectKlass klass, Object[] fields, byte[] primitiveFields) { - assert klass != null; - this.klass = klass; - this.fields = fields; - this.primitiveFields = primitiveFields; + this.arrayOrForeignObject = null; + this.isForeign = false; } // Constructor for regular objects. - private StaticObject(ObjectKlass klass) { + protected StaticObject(ObjectKlass klass) { assert klass != null; assert klass != klass.getMeta().java_lang_Class; this.klass = klass; - LinkedKlass lk = klass.getLinkedKlass(); - this.fields = lk.getObjectFieldsCount() > 0 ? new Object[lk.getObjectFieldsCount()] : null; - int toAlloc = lk.getInstancePrimitiveToAlloc(); - this.primitiveFields = toAlloc > 0 ? new byte[toAlloc] : null; - initInstanceFields(klass); + this.arrayOrForeignObject = null; + this.isForeign = false; } // Constructor for Class objects. - private StaticObject(Klass klass) { + protected StaticObject(Klass klass) { ObjectKlass guestClass = klass.getMeta().java_lang_Class; this.klass = guestClass; - LinkedKlass lgk = guestClass.getLinkedKlass(); - this.fields = lgk.getObjectFieldsCount() > 0 ? new Object[lgk.getObjectFieldsCount()] : null; - int primitiveFieldCount = lgk.getInstancePrimitiveToAlloc(); - this.primitiveFields = primitiveFieldCount > 0 ? new byte[primitiveFieldCount] : null; - initInstanceFields(guestClass); - if (klass.getContext().getJavaVersion().modulesEnabled()) { - klass.getMeta().java_lang_Class_classLoader.setObject(this, klass.getDefiningClassLoader()); - setModule(klass); - } - klass.getMeta().HIDDEN_MIRROR_KLASS.setHiddenObject(this, klass); - // Will be overriden by JVM_DefineKlass if necessary. - klass.getMeta().HIDDEN_PROTECTION_DOMAIN.setHiddenObject(this, StaticObject.NULL); + this.arrayOrForeignObject = null; + this.isForeign = false; } // Constructor for static fields storage. - private StaticObject(ObjectKlass klass, @SuppressWarnings("unused") Void unused) { + protected StaticObject(ObjectKlass klass, @SuppressWarnings("unused") Void unused) { assert klass != null; this.klass = klass; - LinkedKlass lk = klass.getLinkedKlass(); - this.fields = lk.getStaticObjectFieldsCount() > 0 ? new Object[lk.getStaticObjectFieldsCount()] : null; - int toAlloc = lk.getStaticPrimitiveToAlloc(); - this.primitiveFields = toAlloc > 0 ? new byte[toAlloc] : null; - initStaticFields(klass); + this.arrayOrForeignObject = null; + this.isForeign = false; } /** @@ -168,16 +134,16 @@ private StaticObject(ArrayKlass klass, Object array) { assert array != null; assert !(array instanceof StaticObject); assert array.getClass().isArray(); - this.fields = array; - this.primitiveFields = null; + this.arrayOrForeignObject = array; + this.isForeign = false; } private StaticObject(Klass klass, Object foreignObject, @SuppressWarnings("unused") Void unused) { this.klass = klass; assert foreignObject != null; assert !(foreignObject instanceof StaticObject) : "Espresso objects cannot be wrapped"; - this.primitiveFields = FOREIGN_OBJECT_MARKER; - this.fields = foreignObject; + this.arrayOrForeignObject = foreignObject; + this.isForeign = true; } @ExplodeLoop @@ -208,17 +174,28 @@ private void initStaticFields(ObjectKlass thisKlass) { public static StaticObject createNew(ObjectKlass klass) { assert !klass.isAbstract() && !klass.isInterface(); - StaticObject newObj = new StaticObject(klass); + StaticObject newObj = klass.getLinkedKlass().getShape(false).getFactory().create(klass); + newObj.initInstanceFields(klass); return trackAllocation(klass, newObj); } public static StaticObject createClass(Klass klass) { - StaticObject newObj = new StaticObject(klass); + ObjectKlass guestClass = klass.getMeta().java_lang_Class; + StaticObject newObj = guestClass.getLinkedKlass().getShape(false).getFactory().create(klass); + newObj.initInstanceFields(guestClass); + if (klass.getContext().getJavaVersion().modulesEnabled()) { + klass.getMeta().java_lang_Class_classLoader.setObject(newObj, klass.getDefiningClassLoader()); + newObj.setModule(klass); + } + klass.getMeta().HIDDEN_MIRROR_KLASS.setHiddenObject(newObj, klass); + // Will be overriden by JVM_DefineKlass if necessary. + klass.getMeta().HIDDEN_PROTECTION_DOMAIN.setHiddenObject(newObj, StaticObject.NULL); return trackAllocation(klass, newObj); } public static StaticObject createStatics(ObjectKlass klass) { - StaticObject newObj = new StaticObject(klass, null); + StaticObject newObj = klass.getLinkedKlass().getShape(true).getFactory().create(klass, null); + newObj.initStaticFields(klass); return trackAllocation(klass, newObj); } @@ -268,7 +245,11 @@ public StaticObject copy() { if (getKlass().isArray()) { obj = createArray((ArrayKlass) getKlass(), cloneWrappedArray()); } else { - obj = new StaticObject((ObjectKlass) getKlass(), fields == null ? null : ((Object[]) fields).clone(), primitiveFields == null ? null : primitiveFields.clone()); + try { + return (StaticObject) this.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } } return trackAllocation(getKlass(), obj); } @@ -279,20 +260,6 @@ private static StaticObject trackAllocation(Klass klass, StaticObject obj) { // endregion Constructors - // region Accessors for field accesses of non-array, non-foreign objects - public Object[] getObjectFieldStorage() { - assert !isArray(); - checkNotForeign(); - return castExact(fields, Object[].class); - } - - public byte[] getPrimitiveFieldStorage() { - assert !isArray(); - checkNotForeign(); - return primitiveFields; - } - // endregion Accessors for field accesses of non-array, non-foreign objects - public boolean isString() { return StaticObject.notNull(this) && getKlass() == getKlass().getMeta().java_lang_String; } @@ -371,7 +338,7 @@ public void checkNotForeign() { } public boolean isForeignObject() { - return this.primitiveFields == FOREIGN_OBJECT_MARKER; + return isForeign; } public boolean isEspressoObject() { @@ -380,7 +347,7 @@ public boolean isEspressoObject() { public Object rawForeignObject() { assert isForeignObject(); - return this.fields; + return this.arrayOrForeignObject; } public boolean isStaticStorage() { @@ -469,7 +436,7 @@ public String toVerboseString() { public T unwrap() { checkNotForeign(); assert isArray(); - return (T) fields; + return (T) arrayOrForeignObject; } public T get(int index) { @@ -479,7 +446,7 @@ public T get(int index) { } public void putObjectUnsafe(StaticObject value, int index) { - UNSAFE.putObject(fields, (long) getObjectArrayOffset(index), value); + UNSAFE.putObject(arrayOrForeignObject, (long) getObjectArrayOffset(index), value); } public void putObject(StaticObject value, int index, Meta meta) { @@ -529,9 +496,9 @@ public void setArrayByte(byte value, int index, Meta meta) { public void setArrayByte(byte value, int index, Meta meta, BytecodeNode bytecodeNode) { checkNotForeign(); - assert isArray() && fields instanceof byte[]; + assert isArray() && arrayOrForeignObject instanceof byte[]; if (index >= 0 && index < length()) { - UNSAFE.putByte(fields, getByteArrayOffset(index), value); + UNSAFE.putByte(arrayOrForeignObject, getByteArrayOffset(index), value); } else { if (bytecodeNode != null) { bytecodeNode.enterImplicitExceptionProfile(); @@ -546,9 +513,9 @@ public byte getArrayByte(int index, Meta meta) { public byte getArrayByte(int index, Meta meta, BytecodeNode bytecodeNode) { checkNotForeign(); - assert isArray() && fields instanceof byte[]; + assert isArray() && arrayOrForeignObject instanceof byte[]; if (index >= 0 && index < length()) { - return UNSAFE.getByte(fields, getByteArrayOffset(index)); + return UNSAFE.getByte(arrayOrForeignObject, getByteArrayOffset(index)); } else { if (bytecodeNode != null) { bytecodeNode.enterImplicitExceptionProfile(); @@ -560,31 +527,31 @@ public byte getArrayByte(int index, Meta meta, BytecodeNode bytecodeNode) { public int length() { checkNotForeign(); assert isArray(); - return Array.getLength(fields); + return Array.getLength(arrayOrForeignObject); } private Object cloneWrappedArray() { checkNotForeign(); assert isArray(); - if (fields instanceof byte[]) { + if (arrayOrForeignObject instanceof byte[]) { return this. unwrap().clone(); } - if (fields instanceof char[]) { + if (arrayOrForeignObject instanceof char[]) { return this. unwrap().clone(); } - if (fields instanceof short[]) { + if (arrayOrForeignObject instanceof short[]) { return this. unwrap().clone(); } - if (fields instanceof int[]) { + if (arrayOrForeignObject instanceof int[]) { return this. unwrap().clone(); } - if (fields instanceof float[]) { + if (arrayOrForeignObject instanceof float[]) { return this. unwrap().clone(); } - if (fields instanceof double[]) { + if (arrayOrForeignObject instanceof double[]) { return this. unwrap().clone(); } - if (fields instanceof long[]) { + if (arrayOrForeignObject instanceof long[]) { return this. unwrap().clone(); } return this. unwrap().clone(); @@ -659,4 +626,14 @@ public static StaticObject wrapPrimitiveArray(Object array, Meta meta) { public boolean isArray() { return !isNull(this) && getKlass().isArray(); } + + // region Factory interface. + public interface StaticObjectFactory { + StaticObject create(ObjectKlass klass); + + StaticObject create(Klass klass); + + StaticObject create(ObjectKlass klass, Void unused); + } + // endregion Factory interface. } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java index e0e60a16cba6..3f5a80a7848c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java @@ -23,96 +23,89 @@ package com.oracle.truffle.espresso.substitutions; -import java.util.Arrays; - import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.espresso.analysis.liveness.LivenessAnalysis; -import com.oracle.truffle.espresso.impl.Field; -import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.LinkedKlass; import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.impl.ObjectKlass; -import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.StaticObject; -import sun.misc.Unsafe; - @EspressoSubstitutions public class Target_com_oracle_truffle_espresso_InternalUtils { - @Substitution - public static @Host(String[].class) StaticObject getUnderlyingPrimitiveFieldArray(@Host(Class.class) StaticObject clazz) { - int i = 0; - Klass k = clazz.getMirrorKlass(); - int maxLen; - if (k instanceof ObjectKlass) { - maxLen = ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); - } else { - return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), StaticObject.EMPTY_ARRAY); - } - StaticObject[] result = new StaticObject[maxLen]; - Meta meta = k.getMeta(); - StaticObject unused = meta.toGuestString("<>"); - Arrays.fill(result, unused); - try { - while (true) { - Field f = k.lookupFieldTable(i); - if (!f.isStatic() && f.getKind().isPrimitive()) { - for (int j = f.getOffset(); j < f.getOffset() + f.getKind().getByteCount(); j++) { - result[j] = meta.toGuestString(f.getName()); - } - } - i++; - } - } catch (AssertionError | IndexOutOfBoundsException e) { - - } - return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), result); - } - - @Substitution - public static int getPrimitiveFieldByteCount(@Host(Class.class) StaticObject clazz) { - Klass k = clazz.getMirrorKlass(); - if (k instanceof ObjectKlass) { - return ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); - } else { - return 0; - } - } +// @Substitution +// public static @Host(String[].class) StaticObject +// getUnderlyingPrimitiveFieldArray(@Host(Class.class) StaticObject clazz) { +// int i = 0; +// Klass k = clazz.getMirrorKlass(); +// int maxLen; +// if (k instanceof ObjectKlass) { +// maxLen = ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); +// } else { +// return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), +// StaticObject.EMPTY_ARRAY); +// } +// StaticObject[] result = new StaticObject[maxLen]; +// Meta meta = k.getMeta(); +// StaticObject unused = meta.toGuestString("<>"); +// Arrays.fill(result, unused); +// try { +// while (true) { +// Field f = k.lookupFieldTable(i); +// if (!f.isStatic() && f.getKind().isPrimitive()) { +// for (int j = f.getOffset(); j < f.getOffset() + f.getKind().getByteCount(); j++) { +// result[j] = meta.toGuestString(f.getName()); +// } +// } +// i++; +// } +// } catch (AssertionError | IndexOutOfBoundsException e) { +// +// } +// return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), result); +// } +// +// @Substitution +// public static int getPrimitiveFieldByteCount(@Host(Class.class) StaticObject clazz) { +// Klass k = clazz.getMirrorKlass(); +// if (k instanceof ObjectKlass) { +// return ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); +// } else { +// return 0; +// } +// } @Substitution public static @Host(String.class) StaticObject toVerboseString(@Host(Object.class) StaticObject self) { return self.getKlass().getMeta().toGuestString(self.toVerboseString()); } - @Substitution - public static int bytesUsed(@Host(Class.class) StaticObject clazz) { - Klass k = clazz.getMirrorKlass(); - int total = 0; - if (k.isArray()) { - // ArrayKlass reference - total += JavaKind.Int.getByteCount(); - // null reference for primitive field array - total += JavaKind.Int.getByteCount(); - // Header of the Object field array + storing its reference - total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); - return total; - } else { - LinkedKlass lk = ((ObjectKlass) k).getLinkedKlass(); - // Bytes used by the primitive fields - total += lk.getPrimitiveInstanceFieldLastOffset(); - // Bytes used by the Object fields - total += lk.getObjectFieldsCount() * JavaKind.Int.getByteCount(); - // Header of the primitive field array + storing its reference - total += Unsafe.ARRAY_BYTE_BASE_OFFSET + JavaKind.Int.getByteCount(); - // Header of the Object field array + storing its reference - total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); - // Reference to the Klass object. - total += JavaKind.Int.getByteCount(); - return total; - } - } +// @Substitution +// public static int bytesUsed(@Host(Class.class) StaticObject clazz) { +// Klass k = clazz.getMirrorKlass(); +// int total = 0; +// if (k.isArray()) { +// // ArrayKlass reference +// total += JavaKind.Int.getByteCount(); +// // null reference for primitive field array +// total += JavaKind.Int.getByteCount(); +// // Header of the Object field array + storing its reference +// total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); +// return total; +// } else { +// LinkedKlass lk = ((ObjectKlass) k).getLinkedKlass(); +// // Bytes used by the primitive fields +// total += lk.getPrimitiveInstanceFieldLastOffset(); +// // Bytes used by the Object fields +// total += lk.getObjectFieldsCount() * JavaKind.Int.getByteCount(); +// // Header of the primitive field array + storing its reference +// total += Unsafe.ARRAY_BYTE_BASE_OFFSET + JavaKind.Int.getByteCount(); +// // Header of the Object field array + storing its reference +// total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); +// // Reference to the Klass object. +// total += JavaKind.Int.getByteCount(); +// return total; +// } +// } @Substitution public static boolean inEspresso() { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_misc_Unsafe.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_misc_Unsafe.java index 2ea32ab04a77..ba66a1803bf3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_misc_Unsafe.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_sun_misc_Unsafe.java @@ -141,7 +141,7 @@ private static ObjectKlass defineAnonymousKlass(ParserKlass parserKlass, Espress linkedInterfaces[i] = interf.getLinkedKlass(); } - LinkedKlass linkedKlass = new LinkedKlass(parserKlass, superKlass == null ? null : superKlass.getLinkedKlass(), linkedInterfaces); + LinkedKlass linkedKlass = LinkedKlass.create(parserKlass, superKlass == null ? null : superKlass.getLinkedKlass(), linkedInterfaces); ObjectKlass klass = new ObjectKlass(context, linkedKlass, superKlass, superInterfaces, classLoader, hostKlass); From 30842e0ab8cd7cd36242face2d4cbff48c91d406 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Sun, 28 Feb 2021 12:45:00 +0100 Subject: [PATCH 157/290] Controversial: do not reuse shapes on class redefinition. --- .../src/com/oracle/truffle/espresso/impl/ObjectKlass.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index ff07c2f97db8..359e6c27cbc5 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1099,7 +1099,8 @@ public void redefineClass(ChangePacket packet, List refreshSubClass for (int i = 0; i < superInterfaces.length; i++) { interfaces[i] = superInterfaces[i].getLinkedKlass(); } - LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); +// LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); + LinkedKlass linkedKlass = LinkedKlass.create(parserKlass, getSuperKlass().getLinkedKlass(), interfaces); // fields if (!change.getOuterFields().isEmpty()) { From 1b84b3dae64b40989fc667a559105a329f5c5d20 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 3 Mar 2021 17:01:56 +0100 Subject: [PATCH 158/290] Support for Native Image. --- espresso/mx.espresso/reflectconfig.json | 13 ++++ .../ArrayBasedShapeGenerator.java | 60 ++++++++++++++--- .../truffle/espresso/impl/ObjectKlass.java | 3 +- .../espresso/runtime/StaticObject.java | 64 +++++++++++++++++-- 4 files changed, 124 insertions(+), 16 deletions(-) diff --git a/espresso/mx.espresso/reflectconfig.json b/espresso/mx.espresso/reflectconfig.json index e60de58188ea..ed4519420e17 100644 --- a/espresso/mx.espresso/reflectconfig.json +++ b/espresso/mx.espresso/reflectconfig.json @@ -23,5 +23,18 @@ "fields": [ { "name": "VM_SUPPORTS_LONG_CAS" } ] + }, + { + "name": "com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObjectFactory", + "methods": [ + { "name" : "", "parameterTypes": ["int", "int"] } + ] + }, + { + "name": "com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObject", + "fields": [ + { "name": "primitive", "allowUnsafeAccess": true }, + { "name": "object", "allowUnsafeAccess": true } + ] } ] diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 2bd21fd4665d..b6ea3bcf79bf 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; +import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.impl.asm.ClassVisitor; import com.oracle.truffle.api.impl.asm.ClassWriter; import com.oracle.truffle.api.impl.asm.Label; @@ -62,16 +63,40 @@ import static com.oracle.truffle.api.impl.asm.Opcodes.V1_8; final class ArrayBasedShapeGenerator extends ShapeGenerator { - + private static final int UNINITIALIZED_NATIVE_OFFSET = -1; private static final ConcurrentHashMap, Class>, ArrayBasedShapeGenerator> generatorCache = new ConcurrentHashMap<>(); private final int byteArrayOffset; private final int objectArrayOffset; + static { + if (TruffleOptions.AOT) { + try { + Class defaultStorageSuperClass = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject"); + Class defaultStorageFactoryInterface = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject$StaticObjectFactory"); + Class defaultStorageClass = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObject"); + Class defaultFactoryClass = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObjectFactory"); + Collection storageProperties = generateStorageProperties(); + // The offsets of the byte and object arrays cannot be computed at image build time. + // They would refer to a Java object, not to a Native object. + ArrayBasedShapeGenerator sg = new ArrayBasedShapeGenerator(defaultStorageClass, defaultFactoryClass, storageProperties, UNINITIALIZED_NATIVE_OFFSET, + UNINITIALIZED_NATIVE_OFFSET); + generatorCache.putIfAbsent(Pair.create(defaultStorageSuperClass, defaultStorageFactoryInterface), sg); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + private ArrayBasedShapeGenerator(ArrayBasedShapeGenerator rootSG, Collection extendedProperties, StaticShape parentShape) { super(rootSG.generatedStorageClass, rootSG.generatedFactoryClass, extendedProperties, parentShape); - byteArrayOffset = rootSG.byteArrayOffset; - objectArrayOffset = rootSG.objectArrayOffset; + if (TruffleOptions.AOT) { + byteArrayOffset = rootSG.byteArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "primitive") : rootSG.byteArrayOffset; + objectArrayOffset = rootSG.objectArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "object") : rootSG.objectArrayOffset; + } else { + byteArrayOffset = rootSG.byteArrayOffset; + objectArrayOffset = rootSG.objectArrayOffset; + } } private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties) { @@ -80,11 +105,19 @@ private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, int byteArrayOffset, + int objectArrayOffset) { + super(generatedStorageClass, generatedFactoryClass, extendedProperties, null); + this.byteArrayOffset = byteArrayOffset; + this.objectArrayOffset = objectArrayOffset; + } + @SuppressWarnings("unchecked") static ArrayBasedShapeGenerator getShapeGenerator(StaticShape parentShape, Collection extendedProperties) { Class parentStorageSuperClass = parentShape.getStorageClass().getSuperclass(); Class parentStorageFactoryInterface = parentShape.getFactoryInterface(); ArrayBasedShapeGenerator rootSG = (ArrayBasedShapeGenerator) generatorCache.get(Pair.create(parentStorageSuperClass, parentStorageFactoryInterface)); + assert rootSG != null; return new ArrayBasedShapeGenerator<>(rootSG, extendedProperties, parentShape); } @@ -93,17 +126,18 @@ static ArrayBasedShapeGenerator getShapeGenerator(Class storageSuperCl Pair, Class> pair = Pair.create(storageSuperClass, storageFactoryInterface); ArrayBasedShapeGenerator sg = (ArrayBasedShapeGenerator) generatorCache.get(pair); if (sg == null) { + assert !TruffleOptions.AOT; Class generatedStorageClass = generateStorage(storageSuperClass); Class generatedFactoryClass = generateFactory(generatedStorageClass, storageFactoryInterface); sg = new ArrayBasedShapeGenerator<>(generatedStorageClass, generatedFactoryClass, extendedProperties); ArrayBasedShapeGenerator prevSg = (ArrayBasedShapeGenerator) generatorCache.putIfAbsent(pair, sg); - if (prevSg != null) { + if (prevSg == null) { + return sg; + } else { sg = prevSg; } - } else { - sg = new ArrayBasedShapeGenerator<>(sg, extendedProperties, null); } - return sg; + return new ArrayBasedShapeGenerator<>(sg, extendedProperties, null); } private static int getObjectFieldOffset(Class c, String fieldName) { @@ -180,8 +214,10 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, } private static void addCloneMethod(ClassVisitor cv, String className) { + // TODO(da): should the descriptor and the exceptions match those of `super.clone()`? MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "clone", "()Ljava/lang/Object;", null, new String[]{"java/lang/CloneNotSupportedException"}); mv.visitVarInsn(ALOAD, 0); + // TODO(da): we need to call `super.clone()`, not `java.lang.Object.clone()` mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "clone", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, 1); @@ -280,12 +316,16 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl } } + private static Collection generateStorageProperties() { + return Arrays.asList( + new ExtendedProperty(new StaticProperty(StaticPropertyKind.BYTE_ARRAY), "primitive", true), + new ExtendedProperty(new StaticProperty(StaticPropertyKind.OBJECT_ARRAY), "object", true)); + } + private static Class generateStorage(Class storageSuperClass) { String storageSuperName = Type.getInternalName(storageSuperClass); String storageName = generateStorageName(storageSuperClass); - Collection arrayProperties = Arrays.asList( - new ExtendedProperty(new StaticProperty(StaticPropertyKind.BYTE_ARRAY), "primitive", true), - new ExtendedProperty(new StaticProperty(StaticPropertyKind.OBJECT_ARRAY), "object", true)); + Collection arrayProperties = generateStorageProperties(); int classWriterFlags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS; ClassWriter storageWriter = new ClassWriter(classWriterFlags); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index 359e6c27cbc5..af429fdb0353 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1099,7 +1099,8 @@ public void redefineClass(ChangePacket packet, List refreshSubClass for (int i = 0; i < superInterfaces.length; i++) { interfaces[i] = superInterfaces[i].getLinkedKlass(); } -// LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); + // LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, + // getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); LinkedKlass linkedKlass = LinkedKlass.create(parserKlass, getSuperKlass().getLinkedKlass(), interfaces); // fields diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index f27f7339e065..da7262386523 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -245,15 +245,21 @@ public StaticObject copy() { if (getKlass().isArray()) { obj = createArray((ArrayKlass) getKlass(), cloneWrappedArray()); } else { - try { - return (StaticObject) this.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } + obj = (StaticObject) clone(); } return trackAllocation(getKlass(), obj); } + @Override + @TruffleBoundary + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + private static StaticObject trackAllocation(Klass klass, StaticObject obj) { return klass.getContext().trackAllocation(obj); } @@ -636,4 +642,52 @@ public interface StaticObjectFactory { StaticObject create(ObjectKlass klass, Void unused); } // endregion Factory interface. + + public static final class DefaultArrayBasedStaticObject extends StaticObject { + public final byte[] primitive; + public final Object[] object; + + private DefaultArrayBasedStaticObject(ObjectKlass klass, int primitiveArraySize, int objectArraySize) { + super(klass); + primitive = new byte[primitiveArraySize]; + object = new Object[objectArraySize]; + } + + private DefaultArrayBasedStaticObject(Klass klass, int primitiveArraySize, int objectArraySize) { + super(klass); + primitive = new byte[primitiveArraySize]; + object = new Object[objectArraySize]; + } + + private DefaultArrayBasedStaticObject(ObjectKlass klass, Void unused, int primitiveArraySize, int objectArraySize) { + super(klass, unused); + primitive = new byte[primitiveArraySize]; + object = new Object[objectArraySize]; + } + } + + public static final class DefaultArrayBasedStaticObjectFactory implements StaticObjectFactory { + private final int primitiveArraySize; + private final int objectArraySize; + + public DefaultArrayBasedStaticObjectFactory(int primitiveArraySize, int objectArraySize) { + this.primitiveArraySize = primitiveArraySize; + this.objectArraySize = objectArraySize; + } + + @Override + public StaticObject create(ObjectKlass klass) { + return new DefaultArrayBasedStaticObject(klass, primitiveArraySize, objectArraySize); + } + + @Override + public StaticObject create(Klass klass) { + return new DefaultArrayBasedStaticObject(klass, primitiveArraySize, objectArraySize); + } + + @Override + public StaticObject create(ObjectKlass klass, Void unused) { + return new DefaultArrayBasedStaticObject(klass, unused, primitiveArraySize, objectArraySize); + } + } } From a22e6cdb0d64c70bcd8e274f8abca4f227256c26 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 4 Mar 2021 13:29:27 +0100 Subject: [PATCH 159/290] Generated classes are not in the super class package. --- .../staticobject/ArrayBasedShapeGenerator.java | 2 +- .../espresso/staticobject/ShapeGenerator.java | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index b6ea3bcf79bf..79f8319eb243 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -324,7 +324,7 @@ private static Collection generateStorageProperties() { private static Class generateStorage(Class storageSuperClass) { String storageSuperName = Type.getInternalName(storageSuperClass); - String storageName = generateStorageName(storageSuperClass); + String storageName = generateStorageName(); Collection arrayProperties = generateStorageProperties(); int classWriterFlags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 573d0307c6e4..ef797de47178 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -64,16 +64,8 @@ static ShapeGenerator getShapeGenerator(Class storageSuperClass, Class return ArrayBasedShapeGenerator.getShapeGenerator(storageSuperClass, storageFactoryInterface, extendedProperties); } - static String generateStorageName(Class storageSuperClass) { - String internalStorageSuperClassName = Type.getInternalName(storageSuperClass); - String baseName; - int index = internalStorageSuperClassName.indexOf(DELIMITER); - if (index == -1) { - baseName = internalStorageSuperClassName + DELIMITER; - } else { - baseName = internalStorageSuperClassName.substring(0, index + DELIMITER.length()); - } - return baseName + counter.incrementAndGet(); + static String generateStorageName() { + return ShapeGenerator.class.getPackage().getName().replace('.', '/') + "/GeneratedStaticObject" + DELIMITER + counter.incrementAndGet(); } static String generateFactoryName(Class generatedStorageClass) { From e75ca1284779f0d7d862c29ddd37c9618f9941a7 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 4 Mar 2021 13:44:03 +0100 Subject: [PATCH 160/290] Support JDK11. --- .../espresso/staticobject/ShapeGenerator.java | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index ef797de47178..1fe31f78a646 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -27,7 +27,12 @@ import com.oracle.truffle.api.impl.asm.Type; import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import sun.misc.Unsafe; @@ -39,12 +44,31 @@ abstract class ShapeGenerator { protected static final Unsafe UNSAFE = getUnsafe(); private static final String DELIMITER = "$$"; private static final AtomicInteger counter = new AtomicInteger(); + private static final int JAVA_SPEC_VERSION; + private static final Method DEFINE_CLASS; protected final Class generatedStorageClass; protected final Class generatedFactoryClass; protected final Collection extendedProperties; protected final StaticShape parentShape; + static { + String value = System.getProperty("java.specification.version"); + if (value.startsWith("1.")) { + value = value.substring(2); + } + JAVA_SPEC_VERSION = Integer.parseInt(value); + try { + if (JAVA_SPEC_VERSION == 8) { + DEFINE_CLASS = Unsafe.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class); + } else { + DEFINE_CLASS = Lookup.class.getDeclaredMethod("defineClass", byte[].class); + } + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + ShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, StaticShape parentShape) { this.generatedStorageClass = generatedStorageClass; this.generatedFactoryClass = generatedFactoryClass; @@ -85,7 +109,17 @@ static void addStorageFields(ClassVisitor cv, Collection exten @SuppressWarnings("unchecked") static Class load(String name, byte[] bytes, Class referenceClass) { - return (Class) UNSAFE.defineClass(name, bytes, 0, bytes.length, referenceClass.getClassLoader(), referenceClass.getProtectionDomain()); + Object clazz; + try { + if (JAVA_SPEC_VERSION == 8) { + clazz = DEFINE_CLASS.invoke(UNSAFE, name, bytes, 0, bytes.length, referenceClass.getClassLoader(), referenceClass.getProtectionDomain()); + } else { + clazz = DEFINE_CLASS.invoke(MethodHandles.lookup(), bytes); + } + } catch(IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + return (Class) clazz; } private static Unsafe getUnsafe() { From d45e243487f8150d66ac01eba5793637f682336e Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 12 Mar 2021 17:10:48 +0100 Subject: [PATCH 161/290] Remove unsafe accesses from StaticObject. --- .../EspressoReferenceArrayStoreNode.java | 41 +++++--- .../espresso/runtime/JDWPContextImpl.java | 2 +- .../espresso/runtime/StaticObject.java | 97 ------------------- .../truffle/espresso/vm/InterpreterToVM.java | 59 +++++++++-- .../oracle/truffle/espresso/vm/StackWalk.java | 2 +- 5 files changed, 79 insertions(+), 122 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java index 946525035ba4..2bbaab474f49 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java @@ -31,9 +31,12 @@ import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; +import static com.oracle.truffle.espresso.vm.InterpreterToVM.instanceOf; + public class EspressoReferenceArrayStoreNode extends Node { @Child TypeCheckNode typeCheck; - @CompilationFinal boolean noOutOfBoundEx = true; + @CompilationFinal boolean noOutOfBoundEx1 = true; + @CompilationFinal boolean noOutOfBoundEx2 = true; @CompilationFinal boolean noArrayStoreEx = true; public EspressoReferenceArrayStoreNode(EspressoContext context) { @@ -41,27 +44,39 @@ public EspressoReferenceArrayStoreNode(EspressoContext context) { } public void arrayStore(StaticObject value, int index, StaticObject array) { - assert !array.isForeignObject(); - assert array.isArray(); - if (index >= 0 && index < array.length()) { - if (StaticObject.isNull(value) || typeCheck.executeTypeCheck(((ArrayKlass) array.getKlass()).getComponentType(), value.getKlass())) { - array.putObjectUnsafe(value, index); + if (StaticObject.isNull(value) || instanceOf(value, ((ArrayKlass) array.getKlass()).getComponentType())) { + try { + (array.unwrap())[index] = value; + } catch (ArrayIndexOutOfBoundsException e) { + enterOutOfBound1(); + Meta meta = typeCheck.getMeta(); + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + // We must throw ArrayIndexOutOfBoundsException before ArrayStoreException + if (index < 0 || index >= array.length()) { + enterOutOfBound2(); + Meta meta = typeCheck.getMeta(); + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); } else { enterArrayStoreEx(); Meta meta = typeCheck.getMeta(); throw meta.throwException(meta.java_lang_ArrayStoreException); } - } else { - enterOutOfBound(); - Meta meta = typeCheck.getMeta(); - throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); } } - private void enterOutOfBound() { - if (noOutOfBoundEx) { + private void enterOutOfBound1() { + if (noOutOfBoundEx1) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noOutOfBoundEx1 = false; + } + } + + private void enterOutOfBound2() { + if (noOutOfBoundEx2) { CompilerDirectives.transferToInterpreterAndInvalidate(); - noOutOfBoundEx = false; + noOutOfBoundEx2 = false; } } 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 bad0f8849fc1..f8c2087ff860 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 @@ -497,7 +497,7 @@ public Object getArrayValue(Object array, int index) { @Override public void setArrayValue(Object array, int index, Object value) { StaticObject arrayRef = (StaticObject) array; - arrayRef.putObject((StaticObject) value, index, context.getMeta()); + context.getInterpreterToVM().setArrayObject((StaticObject) value, index, arrayRef); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index da7262386523..312849e99834 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -50,8 +50,6 @@ import com.oracle.truffle.espresso.substitutions.Host; import com.oracle.truffle.espresso.vm.UnsafeAccess; -import sun.misc.Unsafe; - /** * Implementation of the Espresso object model. * @@ -66,8 +64,6 @@ public class StaticObject implements TruffleObject, Cloneable { public static final StaticObject NULL = new StaticObject(); public static final String CLASS_TO_STATIC = "static"; - private static final Unsafe UNSAFE = UnsafeAccess.get(); - private final Klass klass; // != PrimitiveKlass private final Object arrayOrForeignObject; @@ -76,12 +72,6 @@ public class StaticObject implements TruffleObject, Cloneable { private volatile EspressoLock lock; - static { - // Assert a byte array has the same representation as a boolean array. - assert (Unsafe.ARRAY_BYTE_BASE_OFFSET == Unsafe.ARRAY_BOOLEAN_BASE_OFFSET && - Unsafe.ARRAY_BYTE_INDEX_SCALE == Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); - } - // region Constructors // Dedicated constructor for NULL. @@ -119,14 +109,6 @@ protected StaticObject(ObjectKlass klass, @SuppressWarnings("unused") Void unuse /** * Constructor for Array objects. - * - * Current implementation stores the array in lieu of fields. fields being an Object, a char - * array can be stored under it without any boxing happening. The array could have been stored - * in fields[0], but getting to the array would then require an additional indirection. - * - * Regular objects still always have an Object[] hiding under fields. In order to preserve the - * behavior and avoid casting to Object[] (a non-leaf cast), we perform field accesses with - * Unsafe operations. */ private StaticObject(ArrayKlass klass, Object array) { this.klass = klass; @@ -451,85 +433,6 @@ public T get(int index) { return this. unwrap()[index]; } - public void putObjectUnsafe(StaticObject value, int index) { - UNSAFE.putObject(arrayOrForeignObject, (long) getObjectArrayOffset(index), value); - } - - public void putObject(StaticObject value, int index, Meta meta) { - putObject(value, index, meta, null); - } - - /** - * Workaround to avoid casting to Object[] in InterpreterToVM (non-leaf type check). - */ - public void putObject(StaticObject value, int index, Meta meta, BytecodeNode bytecodeNode) { - checkNotForeign(); - assert isArray(); - if (index >= 0 && index < length()) { - // TODO(peterssen): Use different profiles for index-out-of-bounds and array-store - // exceptions. - putObjectUnsafe(arrayStoreExCheck(value, ((ArrayKlass) klass).getComponentType(), meta, bytecodeNode), index); - } else { - if (bytecodeNode != null) { - bytecodeNode.enterImplicitExceptionProfile(); - } - throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); - } - } - - private static StaticObject arrayStoreExCheck(StaticObject value, Klass componentType, Meta meta, BytecodeNode bytecodeNode) { - if (StaticObject.isNull(value) || instanceOf(value, componentType)) { - return value; - } else { - if (bytecodeNode != null) { - bytecodeNode.enterImplicitExceptionProfile(); - } - throw meta.throwException(meta.java_lang_ArrayStoreException); - } - } - - public static int getObjectArrayOffset(int index) { - return Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * index; - } - - public static long getByteArrayOffset(int index) { - return Unsafe.ARRAY_BYTE_BASE_OFFSET + Unsafe.ARRAY_BYTE_INDEX_SCALE * (long) index; - } - - public void setArrayByte(byte value, int index, Meta meta) { - setArrayByte(value, index, meta, null); - } - - public void setArrayByte(byte value, int index, Meta meta, BytecodeNode bytecodeNode) { - checkNotForeign(); - assert isArray() && arrayOrForeignObject instanceof byte[]; - if (index >= 0 && index < length()) { - UNSAFE.putByte(arrayOrForeignObject, getByteArrayOffset(index), value); - } else { - if (bytecodeNode != null) { - bytecodeNode.enterImplicitExceptionProfile(); - } - throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); - } - } - - public byte getArrayByte(int index, Meta meta) { - return getArrayByte(index, meta, null); - } - - public byte getArrayByte(int index, Meta meta, BytecodeNode bytecodeNode) { - checkNotForeign(); - assert isArray() && arrayOrForeignObject instanceof byte[]; - if (index >= 0 && index < length()) { - return UNSAFE.getByte(arrayOrForeignObject, getByteArrayOffset(index)); - } else { - if (bytecodeNode != null) { - bytecodeNode.enterImplicitExceptionProfile(); - } - throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); - } - } - public int length() { checkNotForeign(); assert isArray(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java index 5539da126198..b941294c201a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java @@ -192,14 +192,22 @@ public double getArrayDouble(int index, @Host(double[].class) StaticObject array } } - public byte getArrayByte(int index, @Host(byte[].class /* or boolean[] */) StaticObject array, BytecodeNode bytecodeNode) { - return array.getArrayByte(index, getMeta(), bytecodeNode); - } - public byte getArrayByte(int index, @Host(byte[].class /* or boolean[] */) StaticObject array) { return getArrayByte(index, array, null); } + public byte getArrayByte(int index, @Host(byte[].class /* or boolean[] */) StaticObject array, BytecodeNode bytecodeNode) { + try { + return (array. unwrap())[index]; + } catch (ArrayIndexOutOfBoundsException e) { + if (bytecodeNode != null) { + bytecodeNode.enterImplicitExceptionProfile(); + } + Meta meta = getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_ArrayIndexOutOfBoundsException, e.getMessage()); + } + } + public char getArrayChar(int index, @Host(char[].class) StaticObject array) { return getArrayChar(index, array, null); } @@ -305,10 +313,15 @@ public void setArrayByte(byte value, int index, @Host(byte[].class /* or boolean } public void setArrayByte(byte value, int index, @Host(byte[].class /* or boolean[] */) StaticObject array, BytecodeNode bytecodeNode) { - if (getJavaVersion().java9OrLater() && array.getKlass() == getMeta()._boolean_array) { - array.setArrayByte((byte) (value & 1), index, getMeta(), bytecodeNode); - } else { - array.setArrayByte(value, index, getMeta(), bytecodeNode); + byte val = getJavaVersion().java9OrLater() && array.getKlass() == getMeta()._boolean_array ? (byte) (value & 1) : value; + try { + (array. unwrap())[index] = val; + } catch (ArrayIndexOutOfBoundsException e) { + if (bytecodeNode != null) { + bytecodeNode.enterImplicitExceptionProfile(); + } + Meta meta = getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_ArrayIndexOutOfBoundsException, e.getMessage()); } } @@ -345,13 +358,39 @@ public void setArrayShort(short value, int index, @Host(short[].class) StaticObj } public void setArrayObject(StaticObject value, int index, StaticObject wrapper) { - wrapper.putObject(value, index, getMeta(), null); + setArrayObject(value, index, wrapper, null); } public void setArrayObject(StaticObject value, int index, StaticObject wrapper, BytecodeNode bytecodeNode) { - wrapper.putObject(value, index, getMeta(), bytecodeNode); + if (StaticObject.isNull(value) || instanceOf(value, ((ArrayKlass) wrapper.getKlass()).getComponentType())) { + try { + (wrapper. unwrap())[index] = value; + } catch (ArrayIndexOutOfBoundsException e) { + throwArrayIndexOutOfBoundsException(getMeta(), bytecodeNode); + } + } else { + // We must throw ArrayIndexOutOfBoundsException before ArrayStoreException + if (index < 0 || index >= wrapper.length()) { + throwArrayIndexOutOfBoundsException(getMeta(), bytecodeNode); + } else { + throwArrayStoreException(getMeta(), bytecodeNode); + } + } + } + + private static void throwArrayIndexOutOfBoundsException(Meta meta, BytecodeNode bytecodeNode) { + if (bytecodeNode != null) { + bytecodeNode.enterImplicitExceptionProfile(); + } + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); } + private static StaticObject throwArrayStoreException(Meta meta, BytecodeNode bytecodeNode) { + if (bytecodeNode != null) { + bytecodeNode.enterImplicitExceptionProfile(); + } + throw meta.throwException(meta.java_lang_ArrayStoreException); + } // endregion // region Monitor enter/exit diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/StackWalk.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/StackWalk.java index 3d5fee532583..dd9af48d84f3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/StackWalk.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/StackWalk.java @@ -345,7 +345,7 @@ private void processFrame(FrameInstance frameInstance, Method m, int index) { fillFrame(frameInstance, m, index); } else { // Only class info is needed. - frames.putObject(m.getDeclaringKlass().mirror(), index, meta); + meta.getInterpreterToVM().setArrayObject(m.getDeclaringKlass().mirror(), index, frames); } } From ba54cd0a4d22add43d810e9db64effc35b606c06 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Sat, 13 Mar 2021 12:47:28 +0100 Subject: [PATCH 162/290] StaticObject.lock does not need to be volatile. It is initialized only once, in a synchronized block. --- .../src/com/oracle/truffle/espresso/runtime/StaticObject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 312849e99834..1f36060e5f45 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -70,7 +70,7 @@ public class StaticObject implements TruffleObject, Cloneable { private final boolean isForeign; - private volatile EspressoLock lock; + private EspressoLock lock; // region Constructors From 3bd99e24d61c711ff2569976207dfb8d0b5a51ea Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Sat, 13 Mar 2021 12:55:31 +0100 Subject: [PATCH 163/290] Simplify StaticObject constructors. Regressions are expected since some fields are not final anymore. --- .../espresso/runtime/StaticObject.java | 100 +++--------------- 1 file changed, 14 insertions(+), 86 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 1f36060e5f45..9ab3fddd0b7a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -45,7 +45,6 @@ import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; -import com.oracle.truffle.espresso.nodes.BytecodeNode; import com.oracle.truffle.espresso.runtime.dispatch.BaseInterop; import com.oracle.truffle.espresso.substitutions.Host; import com.oracle.truffle.espresso.vm.UnsafeAccess; @@ -61,71 +60,20 @@ public class StaticObject implements TruffleObject, Cloneable { public static final StaticObject[] EMPTY_ARRAY = new StaticObject[0]; - public static final StaticObject NULL = new StaticObject(); + public static final StaticObject NULL = new StaticObject(null); public static final String CLASS_TO_STATIC = "static"; private final Klass klass; // != PrimitiveKlass - private final Object arrayOrForeignObject; + private Object arrayOrForeignObject; - private final boolean isForeign; + private boolean isForeign; private EspressoLock lock; // region Constructors - - // Dedicated constructor for NULL. - private StaticObject() { - assert NULL == null : "Only meant for StaticObject.NULL"; - this.klass = null; - this.arrayOrForeignObject = null; - this.isForeign = false; - } - - // Constructor for regular objects. - protected StaticObject(ObjectKlass klass) { - assert klass != null; - assert klass != klass.getMeta().java_lang_Class; - this.klass = klass; - this.arrayOrForeignObject = null; - this.isForeign = false; - } - - // Constructor for Class objects. protected StaticObject(Klass klass) { - ObjectKlass guestClass = klass.getMeta().java_lang_Class; - this.klass = guestClass; - this.arrayOrForeignObject = null; - this.isForeign = false; - } - - // Constructor for static fields storage. - protected StaticObject(ObjectKlass klass, @SuppressWarnings("unused") Void unused) { - assert klass != null; - this.klass = klass; - this.arrayOrForeignObject = null; - this.isForeign = false; - } - - /** - * Constructor for Array objects. - */ - private StaticObject(ArrayKlass klass, Object array) { this.klass = klass; - assert klass.isArray(); - assert array != null; - assert !(array instanceof StaticObject); - assert array.getClass().isArray(); - this.arrayOrForeignObject = array; - this.isForeign = false; - } - - private StaticObject(Klass klass, Object foreignObject, @SuppressWarnings("unused") Void unused) { - this.klass = klass; - assert foreignObject != null; - assert !(foreignObject instanceof StaticObject) : "Espresso objects cannot be wrapped"; - this.arrayOrForeignObject = foreignObject; - this.isForeign = true; } @ExplodeLoop @@ -163,7 +111,7 @@ public static StaticObject createNew(ObjectKlass klass) { public static StaticObject createClass(Klass klass) { ObjectKlass guestClass = klass.getMeta().java_lang_Class; - StaticObject newObj = guestClass.getLinkedKlass().getShape(false).getFactory().create(klass); + StaticObject newObj = guestClass.getLinkedKlass().getShape(false).getFactory().create(guestClass); newObj.initInstanceFields(guestClass); if (klass.getContext().getJavaVersion().modulesEnabled()) { klass.getMeta().java_lang_Class_classLoader.setObject(newObj, klass.getDefiningClassLoader()); @@ -176,7 +124,7 @@ public static StaticObject createClass(Klass klass) { } public static StaticObject createStatics(ObjectKlass klass) { - StaticObject newObj = klass.getLinkedKlass().getShape(true).getFactory().create(klass, null); + StaticObject newObj = klass.getLinkedKlass().getShape(true).getFactory().create(klass); newObj.initStaticFields(klass); return trackAllocation(klass, newObj); } @@ -186,7 +134,8 @@ public static StaticObject createArray(ArrayKlass klass, Object array) { assert array != null; assert !(array instanceof StaticObject); assert array.getClass().isArray(); - StaticObject newObj = new StaticObject(klass, array); + StaticObject newObj = new StaticObject(klass); + newObj.arrayOrForeignObject = array; return trackAllocation(klass, newObj); } @@ -207,14 +156,19 @@ public static StaticObject createForeign(Klass klass, Object foreignObject, Inte if (interopLibrary.isNull(foreignObject)) { return createForeignNull(foreignObject); } - StaticObject newObj = new StaticObject(klass, foreignObject, null); + StaticObject newObj = new StaticObject(klass); + newObj.arrayOrForeignObject = foreignObject; + newObj.isForeign = true; return trackAllocation(klass, newObj); } public static StaticObject createForeignNull(Object foreignObject) { assert foreignObject != null; assert InteropLibrary.getUncached().isNull(foreignObject); - return new StaticObject(null, foreignObject, null); + StaticObject newObj = new StaticObject(null); + newObj.arrayOrForeignObject = foreignObject; + newObj.isForeign = true; + return newObj; } // Shallow copy. @@ -538,11 +492,7 @@ public boolean isArray() { // region Factory interface. public interface StaticObjectFactory { - StaticObject create(ObjectKlass klass); - StaticObject create(Klass klass); - - StaticObject create(ObjectKlass klass, Void unused); } // endregion Factory interface. @@ -550,23 +500,11 @@ public static final class DefaultArrayBasedStaticObject extends StaticObject { public final byte[] primitive; public final Object[] object; - private DefaultArrayBasedStaticObject(ObjectKlass klass, int primitiveArraySize, int objectArraySize) { - super(klass); - primitive = new byte[primitiveArraySize]; - object = new Object[objectArraySize]; - } - private DefaultArrayBasedStaticObject(Klass klass, int primitiveArraySize, int objectArraySize) { super(klass); primitive = new byte[primitiveArraySize]; object = new Object[objectArraySize]; } - - private DefaultArrayBasedStaticObject(ObjectKlass klass, Void unused, int primitiveArraySize, int objectArraySize) { - super(klass, unused); - primitive = new byte[primitiveArraySize]; - object = new Object[objectArraySize]; - } } public static final class DefaultArrayBasedStaticObjectFactory implements StaticObjectFactory { @@ -578,19 +516,9 @@ public DefaultArrayBasedStaticObjectFactory(int primitiveArraySize, int objectAr this.objectArraySize = objectArraySize; } - @Override - public StaticObject create(ObjectKlass klass) { - return new DefaultArrayBasedStaticObject(klass, primitiveArraySize, objectArraySize); - } - @Override public StaticObject create(Klass klass) { return new DefaultArrayBasedStaticObject(klass, primitiveArraySize, objectArraySize); } - - @Override - public StaticObject create(ObjectKlass klass, Void unused) { - return new DefaultArrayBasedStaticObject(klass, unused, primitiveArraySize, objectArraySize); - } } } From b980634110f1eb0cc8bd4ba2eccc822ba59c04b5 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Sat, 13 Mar 2021 13:23:23 +0100 Subject: [PATCH 164/290] Generate subtypes of StaticObject also for array and foreign object storage. --- .../espresso/staticobject/StaticProperty.java | 2 +- .../truffle/espresso/impl/ArrayKlass.java | 5 + .../espresso/runtime/StaticObject.java | 94 +++++++++++-------- 3 files changed, 62 insertions(+), 39 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 9cb8bec8a2cc..15f8f2045eae 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -39,7 +39,7 @@ public class StaticProperty { this.internalKind = internalKind; } - protected StaticProperty(StaticPropertyKind kind) { + public StaticProperty(StaticPropertyKind kind) { this(getInternalKind(kind)); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java index 475635dd6df2..623f55fc815d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java @@ -40,6 +40,11 @@ import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.runtime.StaticObject; +import com.oracle.truffle.espresso.runtime.StaticObject.StaticObjectFactory; +import com.oracle.truffle.espresso.staticobject.StaticProperty; +import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; +import com.oracle.truffle.espresso.staticobject.StaticShape; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; import com.oracle.truffle.espresso.substitutions.Host; public final class ArrayKlass extends Klass { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 9ab3fddd0b7a..a6e827056e4c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -46,6 +46,9 @@ import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.dispatch.BaseInterop; +import com.oracle.truffle.espresso.staticobject.StaticProperty; +import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; +import com.oracle.truffle.espresso.staticobject.StaticShape; import com.oracle.truffle.espresso.substitutions.Host; import com.oracle.truffle.espresso.vm.UnsafeAccess; @@ -63,13 +66,19 @@ public class StaticObject implements TruffleObject, Cloneable { public static final StaticObject NULL = new StaticObject(null); public static final String CLASS_TO_STATIC = "static"; - private final Klass klass; // != PrimitiveKlass - - private Object arrayOrForeignObject; + private static final StaticProperty ARRAY_PROPERTY = new StaticProperty(StaticPropertyKind.Object); + private static final StaticProperty FOREIGN_PROPERTY = new StaticProperty(StaticPropertyKind.Object); + private static final StaticShape ARRAY_SHAPE = StaticShape.newBuilder() + .property(ARRAY_PROPERTY, "array", true) + .build(StaticObject.class, StaticObjectFactory.class);; + private static final StaticShape FOREIGN_SHAPE = StaticShape.newBuilder() + .property(FOREIGN_PROPERTY, "foreignObject", true) + .build(StaticObject.class, StaticObjectFactory.class);; + private static final EspressoLock FOREIGN_MARKER = EspressoLock.create(); - private boolean isForeign; + private final Klass klass; // != PrimitiveKlass - private EspressoLock lock; + private EspressoLock lockOrForeignMarker; // region Constructors protected StaticObject(Klass klass) { @@ -134,8 +143,8 @@ public static StaticObject createArray(ArrayKlass klass, Object array) { assert array != null; assert !(array instanceof StaticObject); assert array.getClass().isArray(); - StaticObject newObj = new StaticObject(klass); - newObj.arrayOrForeignObject = array; + StaticObject newObj = ARRAY_SHAPE.getFactory().create(klass); + ARRAY_PROPERTY.setObject(newObj, array); return trackAllocation(klass, newObj); } @@ -156,19 +165,20 @@ public static StaticObject createForeign(Klass klass, Object foreignObject, Inte if (interopLibrary.isNull(foreignObject)) { return createForeignNull(foreignObject); } - StaticObject newObj = new StaticObject(klass); - newObj.arrayOrForeignObject = foreignObject; - newObj.isForeign = true; - return trackAllocation(klass, newObj); + return createForeign(klass, foreignObject); } public static StaticObject createForeignNull(Object foreignObject) { assert foreignObject != null; assert InteropLibrary.getUncached().isNull(foreignObject); - StaticObject newObj = new StaticObject(null); - newObj.arrayOrForeignObject = foreignObject; - newObj.isForeign = true; - return newObj; + return createForeign(null, foreignObject); + } + + private static StaticObject createForeign(Klass klass, Object foreignObject) { + StaticObject newObj = FOREIGN_SHAPE.getFactory().create(klass); + FOREIGN_PROPERTY.setObject(newObj, foreignObject); + newObj.lockOrForeignMarker = FOREIGN_MARKER; + return trackAllocation(klass, newObj); } // Shallow copy. @@ -197,7 +207,11 @@ public Object clone() { } private static StaticObject trackAllocation(Klass klass, StaticObject obj) { - return klass.getContext().trackAllocation(obj); + if (klass == null) { + return obj; + } else { + return klass.getContext().trackAllocation(obj); + } } // endregion Constructors @@ -256,12 +270,12 @@ public EspressoLock getLock() { CompilerDirectives.transferToInterpreter(); throw EspressoError.shouldNotReachHere("StaticObject.NULL.getLock()"); } - EspressoLock l = lock; + EspressoLock l = lockOrForeignMarker; if (l == null) { synchronized (this) { - l = lock; + l = lockOrForeignMarker; if (l == null) { - lock = l = EspressoLock.create(); + lockOrForeignMarker = l = EspressoLock.create(); } } } @@ -280,7 +294,7 @@ public void checkNotForeign() { } public boolean isForeignObject() { - return isForeign; + return lockOrForeignMarker == FOREIGN_MARKER; } public boolean isEspressoObject() { @@ -289,7 +303,7 @@ public boolean isEspressoObject() { public Object rawForeignObject() { assert isForeignObject(); - return this.arrayOrForeignObject; + return FOREIGN_PROPERTY.getObject(this); } public boolean isStaticStorage() { @@ -373,12 +387,15 @@ public String toVerboseString() { /** * Start of Array manipulation. */ + private Object getArray() { + return ARRAY_PROPERTY.getObject(this); + } @SuppressWarnings("unchecked") public T unwrap() { checkNotForeign(); assert isArray(); - return (T) arrayOrForeignObject; + return (T) getArray(); } public T get(int index) { @@ -390,34 +407,35 @@ public T get(int index) { public int length() { checkNotForeign(); assert isArray(); - return Array.getLength(arrayOrForeignObject); + return Array.getLength(getArray()); } private Object cloneWrappedArray() { checkNotForeign(); assert isArray(); - if (arrayOrForeignObject instanceof byte[]) { - return this. unwrap().clone(); + Object array = getArray(); + if (array instanceof byte[]) { + return ((byte[]) array).clone(); } - if (arrayOrForeignObject instanceof char[]) { - return this. unwrap().clone(); + if (array instanceof char[]) { + return ((char[]) array).clone(); } - if (arrayOrForeignObject instanceof short[]) { - return this. unwrap().clone(); + if (array instanceof short[]) { + return ((short[]) array).clone(); } - if (arrayOrForeignObject instanceof int[]) { - return this. unwrap().clone(); + if (array instanceof int[]) { + return ((int[]) array).clone(); } - if (arrayOrForeignObject instanceof float[]) { - return this. unwrap().clone(); + if (array instanceof float[]) { + return ((float[]) array).clone(); } - if (arrayOrForeignObject instanceof double[]) { - return this. unwrap().clone(); + if (array instanceof double[]) { + return ((double[]) array).clone(); } - if (arrayOrForeignObject instanceof long[]) { - return this. unwrap().clone(); + if (array instanceof long[]) { + return ((long[]) array).clone(); } - return this. unwrap().clone(); + return ((Object[]) array).clone(); } public static StaticObject wrap(StaticObject[] array, Meta meta) { From 9a8238bec9137da9598529a3db50fc69db909f62 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Sat, 13 Mar 2021 13:36:48 +0100 Subject: [PATCH 165/290] Do not expose generated field offset and storage class. --- .../truffle/espresso/staticobject/StaticProperty.java | 8 +------- .../oracle/truffle/espresso/staticobject/StaticShape.java | 2 +- .../src/com/oracle/truffle/espresso/impl/Field.java | 7 ------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 15f8f2045eae..5820d452c2a9 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -32,6 +32,7 @@ public class StaticProperty { private final byte internalKind; @CompilationFinal // private StaticShape shape; + // The offset is the actual position in the field array of an actual instance. @CompilationFinal // private int offset; @@ -61,13 +62,6 @@ void initShape(StaticShape s) { this.shape = s; } - /** - * The offset is the actual position in the field array of an actual instance. - */ - public int getOffset() { - return offset; - } - byte getInternalKind() { return internalKind; } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 51d156bc2d06..794ccafabef6 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -39,7 +39,7 @@ public final T getFactory() { return factory; } - public final Class getStorageClass() { + final Class getStorageClass() { return storageClass; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java index defca28b5301..ddd5ddf56d4d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java @@ -104,13 +104,6 @@ public int getSlot() { return linkedField.getSlot(); } - /** - * The offset in the field array of an actual instance. - */ - public int getOffset() { - return linkedField.getOffset(); - } - @Override public String toString() { return getDeclaringKlass().getNameAsString() + "." + getName() + ": " + getType(); From b915d73d99f116f984ba756f6e902e1e4983a3d6 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 1 Apr 2021 13:32:29 +0200 Subject: [PATCH 166/290] Move array and foreign object shape and property definitions to ArrayKlass and Klass. --- .../truffle/espresso/impl/ArrayKlass.java | 16 +++++++++++-- .../oracle/truffle/espresso/impl/Klass.java | 17 ++++++++++++++ .../espresso/runtime/StaticObject.java | 23 +++++-------------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java index 623f55fc815d..2155a3afbb9a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, 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 @@ -44,7 +44,6 @@ import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; import com.oracle.truffle.espresso.substitutions.Host; public final class ArrayKlass extends Klass { @@ -53,6 +52,11 @@ public final class ArrayKlass extends Klass { private final Klass elementalType; private final int dimension; + private static final StaticProperty ARRAY_PROPERTY = new StaticProperty(StaticPropertyKind.Object); + private static final StaticShape ARRAY_SHAPE = StaticShape.newBuilder() + .property(ARRAY_PROPERTY, "array", true) + .build(StaticObject.class, StaticObjectFactory.class); + ArrayKlass(Klass componentType) { super(componentType.getContext(), null, // TODO(peterssen): Internal, , or / name? @@ -67,6 +71,14 @@ public final class ArrayKlass extends Klass { this.dimension = Types.getArrayDimensions(getType()); } + public static StaticProperty getArrayProperty() { + return ARRAY_PROPERTY; + } + + public static StaticShape getArrayShape() { + return ARRAY_SHAPE; + } + @Override public int getClassModifiers() { // Arrays (of static inner class) may have protected access. diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index 96e2ac142b43..1e0f337ef4fb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -29,6 +29,9 @@ import java.util.Comparator; import java.util.function.IntFunction; +import com.oracle.truffle.espresso.staticobject.StaticProperty; +import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; +import com.oracle.truffle.espresso.staticobject.StaticShape; import org.graalvm.collections.EconomicSet; import com.oracle.truffle.api.CompilerDirectives; @@ -79,6 +82,7 @@ import com.oracle.truffle.espresso.runtime.EspressoException; import com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics; import com.oracle.truffle.espresso.runtime.StaticObject; +import com.oracle.truffle.espresso.runtime.StaticObject.StaticObjectFactory; import com.oracle.truffle.espresso.runtime.dispatch.BaseInterop; import com.oracle.truffle.espresso.runtime.dispatch.EspressoInterop; import com.oracle.truffle.espresso.substitutions.Host; @@ -466,6 +470,11 @@ public int compare(Klass k1, Klass k2) { static final DebugCounter KLASS_LOOKUP_DECLARED_METHOD_COUNT = DebugCounter.create("Klass.lookupDeclaredMethod call count"); static final DebugCounter KLASS_LOOKUP_DECLARED_FIELD_COUNT = DebugCounter.create("Klass.lookupDeclaredField call count"); + private static final StaticProperty FOREIGN_PROPERTY = new StaticProperty(StaticPropertyKind.Object); + private static final StaticShape FOREIGN_SHAPE = StaticShape.newBuilder() + .property(FOREIGN_PROPERTY, "foreignObject", true) + .build(StaticObject.class, StaticObjectFactory.class); + protected Symbol name; protected Symbol type; private final EspressoContext context; @@ -599,6 +608,14 @@ public final ObjectKlass[] getSuperInterfaces() { this.runtimePackage = initRuntimePackage(); } + public static StaticProperty getForeignProperty() { + return FOREIGN_PROPERTY; + } + + public static StaticShape getForeignShape() { + return FOREIGN_SHAPE; + } + @Override public abstract @Host(ClassLoader.class) StaticObject getDefiningClassLoader(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index a6e827056e4c..ddf07b5ec278 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -46,9 +46,6 @@ import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.dispatch.BaseInterop; -import com.oracle.truffle.espresso.staticobject.StaticProperty; -import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; -import com.oracle.truffle.espresso.staticobject.StaticShape; import com.oracle.truffle.espresso.substitutions.Host; import com.oracle.truffle.espresso.vm.UnsafeAccess; @@ -66,14 +63,6 @@ public class StaticObject implements TruffleObject, Cloneable { public static final StaticObject NULL = new StaticObject(null); public static final String CLASS_TO_STATIC = "static"; - private static final StaticProperty ARRAY_PROPERTY = new StaticProperty(StaticPropertyKind.Object); - private static final StaticProperty FOREIGN_PROPERTY = new StaticProperty(StaticPropertyKind.Object); - private static final StaticShape ARRAY_SHAPE = StaticShape.newBuilder() - .property(ARRAY_PROPERTY, "array", true) - .build(StaticObject.class, StaticObjectFactory.class);; - private static final StaticShape FOREIGN_SHAPE = StaticShape.newBuilder() - .property(FOREIGN_PROPERTY, "foreignObject", true) - .build(StaticObject.class, StaticObjectFactory.class);; private static final EspressoLock FOREIGN_MARKER = EspressoLock.create(); private final Klass klass; // != PrimitiveKlass @@ -143,8 +132,8 @@ public static StaticObject createArray(ArrayKlass klass, Object array) { assert array != null; assert !(array instanceof StaticObject); assert array.getClass().isArray(); - StaticObject newObj = ARRAY_SHAPE.getFactory().create(klass); - ARRAY_PROPERTY.setObject(newObj, array); + StaticObject newObj = ArrayKlass.getArrayShape().getFactory().create(klass); + ArrayKlass.getArrayProperty().setObject(newObj, array); return trackAllocation(klass, newObj); } @@ -175,8 +164,8 @@ public static StaticObject createForeignNull(Object foreignObject) { } private static StaticObject createForeign(Klass klass, Object foreignObject) { - StaticObject newObj = FOREIGN_SHAPE.getFactory().create(klass); - FOREIGN_PROPERTY.setObject(newObj, foreignObject); + StaticObject newObj = Klass.getForeignShape().getFactory().create(klass); + Klass.getForeignProperty().setObject(newObj, foreignObject); newObj.lockOrForeignMarker = FOREIGN_MARKER; return trackAllocation(klass, newObj); } @@ -303,7 +292,7 @@ public boolean isEspressoObject() { public Object rawForeignObject() { assert isForeignObject(); - return FOREIGN_PROPERTY.getObject(this); + return Klass.getForeignProperty().getObject(this); } public boolean isStaticStorage() { @@ -388,7 +377,7 @@ public String toVerboseString() { * Start of Array manipulation. */ private Object getArray() { - return ARRAY_PROPERTY.getObject(this); + return ArrayKlass.getArrayProperty().getObject(this); } @SuppressWarnings("unchecked") From 67edd601b73d69402013ad508cc7e9aabd6e1973 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 6 Apr 2021 19:34:54 +0200 Subject: [PATCH 167/290] Improve assertions. --- .../oracle/truffle/espresso/runtime/StaticObject.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index ddf07b5ec278..3fb63eedfa46 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -101,13 +101,14 @@ private void initStaticFields(ObjectKlass thisKlass) { } public static StaticObject createNew(ObjectKlass klass) { - assert !klass.isAbstract() && !klass.isInterface(); + assert klass != null && !klass.isAbstract() && !klass.isInterface(); StaticObject newObj = klass.getLinkedKlass().getShape(false).getFactory().create(klass); newObj.initInstanceFields(klass); return trackAllocation(klass, newObj); } public static StaticObject createClass(Klass klass) { + assert klass != null; ObjectKlass guestClass = klass.getMeta().java_lang_Class; StaticObject newObj = guestClass.getLinkedKlass().getShape(false).getFactory().create(guestClass); newObj.initInstanceFields(guestClass); @@ -122,6 +123,7 @@ public static StaticObject createClass(Klass klass) { } public static StaticObject createStatics(ObjectKlass klass) { + assert klass != null; StaticObject newObj = klass.getLinkedKlass().getShape(true).getFactory().create(klass); newObj.initStaticFields(klass); return trackAllocation(klass, newObj); @@ -129,6 +131,7 @@ public static StaticObject createStatics(ObjectKlass klass) { // Use an explicit method to create array, avoids confusion. public static StaticObject createArray(ArrayKlass klass, Object array) { + assert klass != null; assert array != null; assert !(array instanceof StaticObject); assert array.getClass().isArray(); @@ -150,20 +153,20 @@ public static StaticObject createArray(ArrayKlass klass, Object array) { } public static StaticObject createForeign(Klass klass, Object foreignObject, InteropLibrary interopLibrary) { - assert foreignObject != null; if (interopLibrary.isNull(foreignObject)) { return createForeignNull(foreignObject); } + assert klass != null; return createForeign(klass, foreignObject); } public static StaticObject createForeignNull(Object foreignObject) { - assert foreignObject != null; assert InteropLibrary.getUncached().isNull(foreignObject); return createForeign(null, foreignObject); } private static StaticObject createForeign(Klass klass, Object foreignObject) { + assert foreignObject != null; StaticObject newObj = Klass.getForeignShape().getFactory().create(klass); Klass.getForeignProperty().setObject(newObj, foreignObject); newObj.lockOrForeignMarker = FOREIGN_MARKER; From 07049d07811b8652c6e15d311b6c4fd9c61e4d53 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 9 Apr 2021 17:34:09 +0200 Subject: [PATCH 168/290] Workarounds to support AOT compilation. --- .../ArrayBasedShapeGenerator.java | 4 +++- .../truffle/espresso/impl/ArrayKlass.java | 19 ++++++++++++++++--- .../oracle/truffle/espresso/impl/Klass.java | 17 ++++++++++++++--- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 79f8319eb243..a8cb0b2954fe 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -72,6 +72,8 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { static { if (TruffleOptions.AOT) { try { + // When this class will be in truffle we will remove this code and introduce a + // SubstrateVM feature that locates these classes at image build time. Class defaultStorageSuperClass = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject"); Class defaultStorageFactoryInterface = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject$StaticObjectFactory"); Class defaultStorageClass = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObject"); @@ -79,7 +81,7 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { Collection storageProperties = generateStorageProperties(); // The offsets of the byte and object arrays cannot be computed at image build time. // They would refer to a Java object, not to a Native object. - ArrayBasedShapeGenerator sg = new ArrayBasedShapeGenerator(defaultStorageClass, defaultFactoryClass, storageProperties, UNINITIALIZED_NATIVE_OFFSET, + ArrayBasedShapeGenerator sg = new ArrayBasedShapeGenerator<>(defaultStorageClass, defaultFactoryClass, storageProperties, UNINITIALIZED_NATIVE_OFFSET, UNINITIALIZED_NATIVE_OFFSET); generatorCache.putIfAbsent(Pair.create(defaultStorageSuperClass, defaultStorageFactoryInterface), sg); } catch (ClassNotFoundException e) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java index 2155a3afbb9a..b532129884ad 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java @@ -29,6 +29,8 @@ import static com.oracle.truffle.espresso.classfile.Constants.ACC_PROTECTED; import static com.oracle.truffle.espresso.classfile.Constants.ACC_PUBLIC; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.classfile.ConstantPool; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Name; @@ -53,9 +55,10 @@ public final class ArrayKlass extends Klass { private final int dimension; private static final StaticProperty ARRAY_PROPERTY = new StaticProperty(StaticPropertyKind.Object); - private static final StaticShape ARRAY_SHAPE = StaticShape.newBuilder() - .property(ARRAY_PROPERTY, "array", true) - .build(StaticObject.class, StaticObjectFactory.class); + // This field should be static final, but until we move the static object model we cannot have a + // SubstrateVM feature which will allow us to set the right field offsets at image build time. + @CompilationFinal // + private static StaticShape ARRAY_SHAPE; ArrayKlass(Klass componentType) { super(componentType.getContext(), @@ -76,9 +79,19 @@ public static StaticProperty getArrayProperty() { } public static StaticShape getArrayShape() { + if (ARRAY_SHAPE == null) { + initializeArrayShape(); + } return ARRAY_SHAPE; } + @TruffleBoundary + private synchronized static void initializeArrayShape() { + if (ARRAY_SHAPE == null) { + ARRAY_SHAPE = StaticShape.newBuilder().property(ARRAY_PROPERTY, "array", true).build(StaticObject.class, StaticObjectFactory.class); + } + } + @Override public int getClassModifiers() { // Arrays (of static inner class) may have protected access. diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index 1e0f337ef4fb..c1511bfe7b37 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -471,9 +471,10 @@ public int compare(Klass k1, Klass k2) { static final DebugCounter KLASS_LOOKUP_DECLARED_FIELD_COUNT = DebugCounter.create("Klass.lookupDeclaredField call count"); private static final StaticProperty FOREIGN_PROPERTY = new StaticProperty(StaticPropertyKind.Object); - private static final StaticShape FOREIGN_SHAPE = StaticShape.newBuilder() - .property(FOREIGN_PROPERTY, "foreignObject", true) - .build(StaticObject.class, StaticObjectFactory.class); + // This field should be static final, but until we move the static object model we cannot have a + // SubstrateVM feature which will allow us to set the right field offsets at image build time. + @CompilationFinal // + private static StaticShape FOREIGN_SHAPE; protected Symbol name; protected Symbol type; @@ -613,9 +614,19 @@ public static StaticProperty getForeignProperty() { } public static StaticShape getForeignShape() { + if (FOREIGN_SHAPE == null) { + initializeForeignShape(); + } return FOREIGN_SHAPE; } + @TruffleBoundary + private synchronized static void initializeForeignShape() { + if (FOREIGN_SHAPE == null) { + FOREIGN_SHAPE = StaticShape.newBuilder().property(FOREIGN_PROPERTY, "foreignObject", true).build(StaticObject.class, StaticObjectFactory.class); + } + } + @Override public abstract @Host(ClassLoader.class) StaticObject getDefiningClassLoader(); From 1592a59708cca9812b1195194bc0414cc07b4d17 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 12 Apr 2021 15:24:15 +0200 Subject: [PATCH 169/290] Improve visibility of the foreign object marker. --- .../espresso/runtime/StaticObject.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 3fb63eedfa46..07d37c50257a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -71,6 +71,15 @@ public class StaticObject implements TruffleObject, Cloneable { // region Constructors protected StaticObject(Klass klass) { + this(klass, false); + } + + protected StaticObject(Klass klass, boolean isForeign) { + if (isForeign) { + // This assignment is visible by all threads as a side-effect of the setting of the + // final `klass` field in the constructor. + lockOrForeignMarker = FOREIGN_MARKER; + } this.klass = klass; } @@ -167,9 +176,8 @@ public static StaticObject createForeignNull(Object foreignObject) { private static StaticObject createForeign(Klass klass, Object foreignObject) { assert foreignObject != null; - StaticObject newObj = Klass.getForeignShape().getFactory().create(klass); + StaticObject newObj = Klass.getForeignShape().getFactory().create(klass, true); Klass.getForeignProperty().setObject(newObj, foreignObject); - newObj.lockOrForeignMarker = FOREIGN_MARKER; return trackAllocation(klass, newObj); } @@ -503,9 +511,12 @@ public boolean isArray() { // region Factory interface. public interface StaticObjectFactory { StaticObject create(Klass klass); + + StaticObject create(Klass klass, boolean isForeign); } // endregion Factory interface. + // This class mimics the layout of generated storage classes public static final class DefaultArrayBasedStaticObject extends StaticObject { public final byte[] primitive; public final Object[] object; @@ -515,8 +526,15 @@ private DefaultArrayBasedStaticObject(Klass klass, int primitiveArraySize, int o primitive = new byte[primitiveArraySize]; object = new Object[objectArraySize]; } + + private DefaultArrayBasedStaticObject(Klass klass, boolean isForeign, int primitiveArraySize, int objectArraySize) { + super(klass, isForeign); + primitive = new byte[primitiveArraySize]; + object = new Object[objectArraySize]; + } } + // This class mimics the layout of generated storage factory classes public static final class DefaultArrayBasedStaticObjectFactory implements StaticObjectFactory { private final int primitiveArraySize; private final int objectArraySize; @@ -530,5 +548,10 @@ public DefaultArrayBasedStaticObjectFactory(int primitiveArraySize, int objectAr public StaticObject create(Klass klass) { return new DefaultArrayBasedStaticObject(klass, primitiveArraySize, objectArraySize); } + + @Override + public StaticObject create(Klass klass, boolean isForeign) { + return new DefaultArrayBasedStaticObject(klass, isForeign, primitiveArraySize, objectArraySize); + } } } From 3e1fd8f4a9a659a357c9a047e8beeb36f58d7baf Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 12 Apr 2021 17:06:50 +0200 Subject: [PATCH 170/290] Refactor constructors. --- .../ArrayBasedShapeGenerator.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index a8cb0b2954fe..28a37249789e 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -91,25 +91,34 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { } private ArrayBasedShapeGenerator(ArrayBasedShapeGenerator rootSG, Collection extendedProperties, StaticShape parentShape) { - super(rootSG.generatedStorageClass, rootSG.generatedFactoryClass, extendedProperties, parentShape); - if (TruffleOptions.AOT) { - byteArrayOffset = rootSG.byteArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "primitive") : rootSG.byteArrayOffset; - objectArrayOffset = rootSG.objectArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "object") : rootSG.objectArrayOffset; - } else { - byteArrayOffset = rootSG.byteArrayOffset; - objectArrayOffset = rootSG.objectArrayOffset; - } + this( + rootSG.generatedStorageClass, + rootSG.generatedFactoryClass, + extendedProperties, + parentShape, + TruffleOptions.AOT && rootSG.byteArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "primitive") : rootSG.byteArrayOffset, + TruffleOptions.AOT && rootSG.objectArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "object") : rootSG.objectArrayOffset); } private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties) { - super(generatedStorageClass, generatedFactoryClass, extendedProperties, null); - byteArrayOffset = getObjectFieldOffset(generatedStorageClass, "primitive"); - objectArrayOffset = getObjectFieldOffset(generatedStorageClass, "object"); + this( + generatedStorageClass, + generatedFactoryClass, + extendedProperties, + null, + getObjectFieldOffset(generatedStorageClass, "primitive"), + getObjectFieldOffset(generatedStorageClass, "object")); } private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, int byteArrayOffset, int objectArrayOffset) { - super(generatedStorageClass, generatedFactoryClass, extendedProperties, null); + this(generatedStorageClass, generatedFactoryClass, extendedProperties, null, byteArrayOffset, objectArrayOffset); + } + + private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, StaticShape parentShape, + int byteArrayOffset, + int objectArrayOffset) { + super(generatedStorageClass, generatedFactoryClass, extendedProperties, parentShape); this.byteArrayOffset = byteArrayOffset; this.objectArrayOffset = objectArrayOffset; } From 0e262d9a04b716f18aa8904bf34f32c7cb005f2f Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 12 Apr 2021 19:40:03 +0200 Subject: [PATCH 171/290] Add shape checks to property accesses. --- espresso/mx.espresso/reflectconfig.json | 3 +- .../ArrayBasedShapeGenerator.java | 61 +++++++++++------ .../staticobject/ArrayBasedStaticShape.java | 67 +++++++++++++++---- .../espresso/staticobject/StaticShape.java | 14 ++-- .../espresso/runtime/StaticObject.java | 23 ++++--- 5 files changed, 121 insertions(+), 47 deletions(-) diff --git a/espresso/mx.espresso/reflectconfig.json b/espresso/mx.espresso/reflectconfig.json index ed4519420e17..9970f4941b0e 100644 --- a/espresso/mx.espresso/reflectconfig.json +++ b/espresso/mx.espresso/reflectconfig.json @@ -27,12 +27,13 @@ { "name": "com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObjectFactory", "methods": [ - { "name" : "", "parameterTypes": ["int", "int"] } + { "name" : "", "parameterTypes": ["java.lang.Object", "int", "int"] } ] }, { "name": "com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObject", "fields": [ + { "name": "shape", "allowUnsafeAccess": true }, { "name": "primitive", "allowUnsafeAccess": true }, { "name": "object", "allowUnsafeAccess": true } ] diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 28a37249789e..aec43463328e 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -68,6 +68,7 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { private final int byteArrayOffset; private final int objectArrayOffset; + private final int shapeOffset; static { if (TruffleOptions.AOT) { @@ -82,7 +83,7 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { // The offsets of the byte and object arrays cannot be computed at image build time. // They would refer to a Java object, not to a Native object. ArrayBasedShapeGenerator sg = new ArrayBasedShapeGenerator<>(defaultStorageClass, defaultFactoryClass, storageProperties, UNINITIALIZED_NATIVE_OFFSET, - UNINITIALIZED_NATIVE_OFFSET); + UNINITIALIZED_NATIVE_OFFSET, UNINITIALIZED_NATIVE_OFFSET); generatorCache.putIfAbsent(Pair.create(defaultStorageSuperClass, defaultStorageFactoryInterface), sg); } catch (ClassNotFoundException e) { throw new RuntimeException(e); @@ -97,7 +98,8 @@ private ArrayBasedShapeGenerator(ArrayBasedShapeGenerator rootSG, Collection< extendedProperties, parentShape, TruffleOptions.AOT && rootSG.byteArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "primitive") : rootSG.byteArrayOffset, - TruffleOptions.AOT && rootSG.objectArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "object") : rootSG.objectArrayOffset); + TruffleOptions.AOT && rootSG.objectArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "object") : rootSG.objectArrayOffset, + TruffleOptions.AOT && rootSG.objectArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "shape") : rootSG.shapeOffset); } private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties) { @@ -107,20 +109,21 @@ private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, int byteArrayOffset, - int objectArrayOffset) { - this(generatedStorageClass, generatedFactoryClass, extendedProperties, null, byteArrayOffset, objectArrayOffset); + int objectArrayOffset, int shapeOffset) { + this(generatedStorageClass, generatedFactoryClass, extendedProperties, null, byteArrayOffset, objectArrayOffset, shapeOffset); } private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, StaticShape parentShape, - int byteArrayOffset, - int objectArrayOffset) { + int byteArrayOffset, int objectArrayOffset, int shapeOffset) { super(generatedStorageClass, generatedFactoryClass, extendedProperties, parentShape); this.byteArrayOffset = byteArrayOffset; this.objectArrayOffset = objectArrayOffset; + this.shapeOffset = shapeOffset; } @SuppressWarnings("unchecked") @@ -161,7 +164,7 @@ private static int getObjectFieldOffset(Class c, String fieldName) { @Override StaticShape generateShape() { - return ArrayBasedStaticShape.create(generatedStorageClass, generatedFactoryClass, parentShape, extendedProperties, byteArrayOffset, objectArrayOffset); + return ArrayBasedStaticShape.create(generatedStorageClass, generatedFactoryClass, (ArrayBasedStaticShape) parentShape, extendedProperties, byteArrayOffset, objectArrayOffset, shapeOffset); } private static String getStorageConstructorDescriptor(Constructor superConstructor) { @@ -170,7 +173,7 @@ private static String getStorageConstructorDescriptor(Constructor superConstr for (Class parameter : superConstructor.getParameterTypes()) { sb.append(Type.getDescriptor(parameter)); } - sb.append("II"); + sb.append("Ljava/lang/Object;II"); return sb.append(")V").toString(); } @@ -189,12 +192,17 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, } mv.visitMethodInsn(INVOKESPECIAL, storageSuperName, "", superConstructorDescriptor, false); + // this.shape = shape; + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, var); + mv.visitFieldInsn(PUTFIELD, storageName, "shape", "Ljava/lang/Object;"); + // primitive = primitiveArraySize > 0 ? new byte[primitiveArraySize] : null; mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ILOAD, var); + mv.visitVarInsn(ILOAD, var + 1); Label label2 = new Label(); mv.visitJumpInsn(IFLE, label2); - mv.visitVarInsn(ILOAD, var); + mv.visitVarInsn(ILOAD, var + 1); mv.visitIntInsn(NEWARRAY, T_BYTE); Label label3 = new Label(); mv.visitJumpInsn(GOTO, label3); @@ -205,10 +213,10 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, // object = objectArraySize > 0 ? new Object[objectArraySize] : null; mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ILOAD, var + 1); + mv.visitVarInsn(ILOAD, var + 2); Label label5 = new Label(); mv.visitJumpInsn(IFLE, label5); - mv.visitVarInsn(ILOAD, var + 1); + mv.visitVarInsn(ILOAD, var + 2); mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); Label label6 = new Label(); mv.visitJumpInsn(GOTO, label6); @@ -218,7 +226,7 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, mv.visitFieldInsn(PUTFIELD, storageName, "object", "[Ljava/lang/Object;"); mv.visitInsn(RETURN); - mv.visitMaxs(Math.max(var, 2), var + 2); + mv.visitMaxs(Math.max(var, 3), var + 3); mv.visitEnd(); } @@ -233,6 +241,12 @@ private static void addCloneMethod(ClassVisitor cv, String className) { mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, 1); + // clone.shape = shape; + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, "shape", "Ljava/lang/Object;"); + mv.visitFieldInsn(PUTFIELD, className, "shape", "Ljava/lang/Object;"); + // clone.primitive = (primitive == null ? null : (byte[]) primitive.clone()); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); @@ -271,25 +285,29 @@ private static void addCloneMethod(ClassVisitor cv, String className) { mv.visitVarInsn(ALOAD, 1); mv.visitInsn(ARETURN); - mv.visitMaxs(2, 2); + mv.visitMaxs(3, 3); mv.visitEnd(); } private static void addFactoryFields(ClassVisitor cv) { + cv.visitField(ACC_PUBLIC | ACC_FINAL, "shape", "Ljava/lang/Object;", null, null).visitEnd(); cv.visitField(ACC_PUBLIC | ACC_FINAL, "primitiveArraySize", "I", null, null).visitEnd(); cv.visitField(ACC_PUBLIC | ACC_FINAL, "objectArraySize", "I", null, null).visitEnd(); } private static void addFactoryConstructor(ClassVisitor cv, String className) { - MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", "(II)V", null, null); + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", "(Ljava/lang/Object;II)V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false); mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ILOAD, 1); - mv.visitFieldInsn(PUTFIELD, className, "primitiveArraySize", "I"); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, className, "shape", "Ljava/lang/Object;"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ILOAD, 2); + mv.visitFieldInsn(PUTFIELD, className, "primitiveArraySize", "I"); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 3); mv.visitFieldInsn(PUTFIELD, className, "objectArraySize", "I"); mv.visitInsn(RETURN); mv.visitMaxs(2, 4); @@ -311,6 +329,10 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl constructorDescriptor.append(Type.getDescriptor(p)); } + constructorDescriptor.append("Ljava/lang/Object;"); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, factoryName, "shape", "Ljava/lang/Object;"); + var++; for (String fieldName : new String[]{"primitiveArraySize", "objectArraySize"}) { constructorDescriptor.append("I"); mv.visitVarInsn(ALOAD, 0); @@ -330,7 +352,8 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl private static Collection generateStorageProperties() { return Arrays.asList( new ExtendedProperty(new StaticProperty(StaticPropertyKind.BYTE_ARRAY), "primitive", true), - new ExtendedProperty(new StaticProperty(StaticPropertyKind.OBJECT_ARRAY), "object", true)); + new ExtendedProperty(new StaticProperty(StaticPropertyKind.OBJECT_ARRAY), "object", true), + new ExtendedProperty(new StaticProperty(StaticPropertyKind.Object), "shape", true)); } private static Class generateStorage(Class storageSuperClass) { diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java index 719c04aaac9a..2e52a753cdcb 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.espresso.staticobject; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; @@ -35,25 +36,50 @@ import static com.oracle.truffle.espresso.staticobject.StaticPropertyKind.N_PRIMITIVES; final class ArrayBasedStaticShape extends StaticShape { - private static final Unsafe UNSAFE; - static { - UNSAFE = getUnsafe(); - } - + private static final Unsafe UNSAFE = getUnsafe(); + @CompilationFinal // + private static Boolean DISABLE_SHAPE_CHECKS; + @CompilationFinal(dimensions = 1) // + private final StaticShape[] superShapes; private final ArrayBasedPropertyLayout propertyLayout; - private ArrayBasedStaticShape(Class storageClass, T factory, ArrayBasedPropertyLayout propertyLayout) { - super(storageClass, factory); + @SuppressWarnings({"unchecked", "rawtypes"}) + private ArrayBasedStaticShape(ArrayBasedStaticShape parentShape, Class storageClass, ArrayBasedPropertyLayout propertyLayout) { + super(storageClass); + if (parentShape == null) { + superShapes = new StaticShape[]{this}; + } else { + int depth = parentShape.superShapes.length; + superShapes = new StaticShape[depth + 1]; + System.arraycopy(parentShape.superShapes, 0, superShapes, 0, depth); + superShapes[depth] = this; + } this.propertyLayout = propertyLayout; } - static ArrayBasedStaticShape create(Class generatedStorageClass, Class generatedFactoryClass, StaticShape parentShape, Collection extendedProperties, - int byteArrayOffset, int objectArrayOffset) { + public static boolean shapeChecks() { + if (DISABLE_SHAPE_CHECKS == null) { + initializeShapeChecks(); + } + return !DISABLE_SHAPE_CHECKS; + } + + @CompilerDirectives.TruffleBoundary + private synchronized static void initializeShapeChecks() { + if (DISABLE_SHAPE_CHECKS == null) { + DISABLE_SHAPE_CHECKS = Boolean.getBoolean("com.oracle.truffle.espresso.staticobject.DisableShapeChecks"); + } + } + + static ArrayBasedStaticShape create(Class generatedStorageClass, Class generatedFactoryClass, ArrayBasedStaticShape parentShape, Collection extendedProperties, + int byteArrayOffset, int objectArrayOffset, int shapeOffset) { try { - ArrayBasedPropertyLayout parentPropertyLayout = parentShape == null ? null : ((ArrayBasedStaticShape) parentShape).getPropertyLayout(); - ArrayBasedPropertyLayout propertyLayout = new ArrayBasedPropertyLayout(parentPropertyLayout, extendedProperties, byteArrayOffset, objectArrayOffset); - T factory = generatedFactoryClass.cast(generatedFactoryClass.getConstructor(int.class, int.class).newInstance(propertyLayout.getPrimitiveArraySize(), propertyLayout.getObjectArraySize())); - return new ArrayBasedStaticShape<>(generatedStorageClass, factory, propertyLayout); + ArrayBasedPropertyLayout parentPropertyLayout = parentShape == null ? null : parentShape.getPropertyLayout(); + ArrayBasedPropertyLayout propertyLayout = new ArrayBasedPropertyLayout(parentPropertyLayout, extendedProperties, byteArrayOffset, objectArrayOffset, shapeOffset); + ArrayBasedStaticShape shape = new ArrayBasedStaticShape<>(parentShape, generatedStorageClass, propertyLayout); + T factory = generatedFactoryClass.cast(generatedFactoryClass.getConstructor(Object.class, int.class, int.class).newInstance(shape, propertyLayout.getPrimitiveArraySize(), propertyLayout.getObjectArraySize())); + shape.setFactory(factory); + return shape; } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } @@ -62,9 +88,20 @@ static ArrayBasedStaticShape create(Class generatedStorageClass, Class @Override Object getStorage(Object obj, boolean primitive) { Object receiverObject = cast(obj, storageClass); + if (shapeChecks()) { + checkShape(receiverObject); + } return UNSAFE.getObject(receiverObject, (long) (primitive ? propertyLayout.byteArrayOffset : propertyLayout.objectArrayOffset)); } + private void checkShape(Object receiverObject) { + ArrayBasedStaticShape receiverShape = cast(UNSAFE.getObject(receiverObject, (long) propertyLayout.shapeOffset), ArrayBasedStaticShape.class); + if (this != receiverShape && (receiverShape.superShapes.length < superShapes.length || receiverShape.superShapes[superShapes.length - 1] != this)) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new RuntimeException("Incompatible shape on property access. Expected '" + this + "' got '" + receiverShape + "'."); + } + } + private ArrayBasedPropertyLayout getPropertyLayout() { return propertyLayout; } @@ -162,10 +199,12 @@ static class ArrayBasedPropertyLayout { private final int byteArrayOffset; private final int objectArrayOffset; + private final int shapeOffset; - ArrayBasedPropertyLayout(ArrayBasedPropertyLayout parentLayout, Collection extendedProperties, int byteArrayOffset, int objectArrayOffset) { + ArrayBasedPropertyLayout(ArrayBasedPropertyLayout parentLayout, Collection extendedProperties, int byteArrayOffset, int objectArrayOffset, int shapeOffset) { this.byteArrayOffset = byteArrayOffset; this.objectArrayOffset = objectArrayOffset; + this.shapeOffset = shapeOffset; // Stats about primitive fields int superTotalByteCount; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 794ccafabef6..044d688232e8 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -24,17 +24,23 @@ public abstract class StaticShape { protected final Class storageClass; - protected final T factory; + protected T factory; - StaticShape(Class storageClass, T factory) { + StaticShape(Class storageClass) { this.storageClass = storageClass; - this.factory = factory; } public static StaticShapeBuilder newBuilder() { return new StaticShapeBuilder(); } + final protected void setFactory(T factory) { + if (this.factory != null) { + throw new RuntimeException("Attempt to reinitialize the offset of a static property. Was it added to more than one builder?"); + } + this.factory = factory; + } + public final T getFactory() { return factory; } @@ -45,7 +51,7 @@ final Class getStorageClass() { abstract Object getStorage(Object obj, boolean primitive); - static Object cast(Object obj, Class type) { + static T cast(Object obj, Class type) { return type.cast(obj); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 07d37c50257a..bc2f4ee76dd1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -518,40 +518,45 @@ public interface StaticObjectFactory { // This class mimics the layout of generated storage classes public static final class DefaultArrayBasedStaticObject extends StaticObject { + public final Object shape; public final byte[] primitive; public final Object[] object; - private DefaultArrayBasedStaticObject(Klass klass, int primitiveArraySize, int objectArraySize) { + private DefaultArrayBasedStaticObject(Klass klass, Object shape, int primitiveArraySize, int objectArraySize) { super(klass); - primitive = new byte[primitiveArraySize]; - object = new Object[objectArraySize]; + this.shape = shape; + this.primitive = new byte[primitiveArraySize]; + this.object = new Object[objectArraySize]; } - private DefaultArrayBasedStaticObject(Klass klass, boolean isForeign, int primitiveArraySize, int objectArraySize) { + private DefaultArrayBasedStaticObject(Klass klass, boolean isForeign, Object shape, int primitiveArraySize, int objectArraySize) { super(klass, isForeign); - primitive = new byte[primitiveArraySize]; - object = new Object[objectArraySize]; + this.shape = shape; + this.primitive = new byte[primitiveArraySize]; + this.object = new Object[objectArraySize]; } } // This class mimics the layout of generated storage factory classes public static final class DefaultArrayBasedStaticObjectFactory implements StaticObjectFactory { + private final Object shape; private final int primitiveArraySize; private final int objectArraySize; - public DefaultArrayBasedStaticObjectFactory(int primitiveArraySize, int objectArraySize) { + public DefaultArrayBasedStaticObjectFactory(Object shape, int primitiveArraySize, int objectArraySize) { + this.shape = shape; this.primitiveArraySize = primitiveArraySize; this.objectArraySize = objectArraySize; } @Override public StaticObject create(Klass klass) { - return new DefaultArrayBasedStaticObject(klass, primitiveArraySize, objectArraySize); + return new DefaultArrayBasedStaticObject(klass, shape, primitiveArraySize, objectArraySize); } @Override public StaticObject create(Klass klass, boolean isForeign) { - return new DefaultArrayBasedStaticObject(klass, isForeign, primitiveArraySize, objectArraySize); + return new DefaultArrayBasedStaticObject(klass, isForeign, shape, primitiveArraySize, objectArraySize); } } } From c07f5e2b84e415471d4d328090e78c5f3969ca16 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 12 Apr 2021 20:13:37 +0200 Subject: [PATCH 172/290] Support class redefinition. --- .../truffle/espresso/impl/LinkedKlass.java | 25 ++++--------------- .../espresso/impl/LinkedKlassFieldLayout.java | 10 ++++++-- .../truffle/espresso/impl/ObjectKlass.java | 4 +-- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java index b2dd7e1c1e66..ab86133cc924 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java @@ -100,7 +100,11 @@ private LinkedKlass(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass } public static LinkedKlass create(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces) { - LinkedKlassFieldLayout fieldLayout = new LinkedKlassFieldLayout(parserKlass, superKlass); + return redefine(parserKlass, superKlass, interfaces, null); + } + + public static LinkedKlass redefine(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces, LinkedKlass redefinedKlass) { + LinkedKlassFieldLayout fieldLayout = new LinkedKlassFieldLayout(parserKlass, superKlass, redefinedKlass); return new LinkedKlass( parserKlass, superKlass, @@ -112,25 +116,6 @@ public static LinkedKlass create(ParserKlass parserKlass, LinkedKlass superKlass fieldLayout.fieldTableLength); } - public static LinkedKlass redefine(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces, LinkedKlass redefinedKlass) { - // On class redefinition we need to re-use the old shape. - // If we don't do it, shape checks on field accesses fail because `Field` instances in - // `ObjectKlass.fieldTable` hold references to the old shape, which does not match the shape - // of the new object instances. - // If we work around this issue by patching the `ObjectKlass.fieldTable` on class - // redefinition, these new `Field` instances cannot be used to access object instances with - // the old shape. - return new LinkedKlass( - parserKlass, - superKlass, - interfaces, - redefinedKlass.instanceShape, - redefinedKlass.staticShape, - redefinedKlass.instanceFields, - redefinedKlass.staticFields, - redefinedKlass.fieldTableLength); - } - int getFlags() { int flags = parserKlass.getFlags(); if (hasFinalizer) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java index 3aab7a9dd5eb..b21ec8535732 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java @@ -45,7 +45,7 @@ final class LinkedKlassFieldLayout { final int fieldTableLength; - LinkedKlassFieldLayout(ParserKlass parserKlass, LinkedKlass superKlass) { + LinkedKlassFieldLayout(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass redefinedKlass) { StaticShapeBuilder instanceBuilder = StaticShape.newBuilder(); StaticShapeBuilder staticBuilder = StaticShape.newBuilder(); @@ -77,7 +77,13 @@ final class LinkedKlassFieldLayout { if (superKlass == null) { instanceShape = instanceBuilder.build(StaticObject.class, StaticObjectFactory.class); } else { - instanceShape = instanceBuilder.build(superKlass.getShape(false)); + if (redefinedKlass == null) { + instanceShape = instanceBuilder.build(superKlass.getShape(false)); + } else { + // The redefining klass is a subshape of the redefined klass. + // This allows shape checks on field accesses using the old StaticProperty instances, but it might not work if the redefined shape has other subshapes. + instanceShape = instanceBuilder.build(redefinedKlass.getShape(false)); + } } staticShape = staticBuilder.build(StaticObject.class, StaticObjectFactory.class); fieldTableLength = nextInstanceFieldSlot; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index af429fdb0353..42f95dae88bd 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1099,9 +1099,7 @@ public void redefineClass(ChangePacket packet, List refreshSubClass for (int i = 0; i < superInterfaces.length; i++) { interfaces[i] = superInterfaces[i].getLinkedKlass(); } - // LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, - // getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); - LinkedKlass linkedKlass = LinkedKlass.create(parserKlass, getSuperKlass().getLinkedKlass(), interfaces); + LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); // fields if (!change.getOuterFields().isEmpty()) { From a41eb12592b9e7d532f562f0a368f37064fc3045 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 12 Apr 2021 20:16:12 +0200 Subject: [PATCH 173/290] Fix style. --- .../espresso/staticobject/ArrayBasedStaticShape.java | 12 ++++++------ .../espresso/staticobject/ShapeGenerator.java | 4 ++-- .../truffle/espresso/staticobject/StaticShape.java | 2 +- .../com/oracle/truffle/espresso/impl/ArrayKlass.java | 12 ++++++------ .../src/com/oracle/truffle/espresso/impl/Klass.java | 12 ++++++------ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java index 2e52a753cdcb..e27983788829 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -38,7 +38,7 @@ final class ArrayBasedStaticShape extends StaticShape { private static final Unsafe UNSAFE = getUnsafe(); @CompilationFinal // - private static Boolean DISABLE_SHAPE_CHECKS; + private static Boolean disableShapeChecks; @CompilationFinal(dimensions = 1) // private final StaticShape[] superShapes; private final ArrayBasedPropertyLayout propertyLayout; @@ -58,16 +58,16 @@ private ArrayBasedStaticShape(ArrayBasedStaticShape parentShape, Class sto } public static boolean shapeChecks() { - if (DISABLE_SHAPE_CHECKS == null) { + if (disableShapeChecks == null) { initializeShapeChecks(); } - return !DISABLE_SHAPE_CHECKS; + return !disableShapeChecks; } @CompilerDirectives.TruffleBoundary - private synchronized static void initializeShapeChecks() { - if (DISABLE_SHAPE_CHECKS == null) { - DISABLE_SHAPE_CHECKS = Boolean.getBoolean("com.oracle.truffle.espresso.staticobject.DisableShapeChecks"); + private static synchronized void initializeShapeChecks() { + if (disableShapeChecks == null) { + disableShapeChecks = Boolean.getBoolean("com.oracle.truffle.espresso.staticobject.DisableShapeChecks"); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 1fe31f78a646..25b999984220 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -64,7 +64,7 @@ abstract class ShapeGenerator { } else { DEFINE_CLASS = Lookup.class.getDeclaredMethod("defineClass", byte[].class); } - } catch(NoSuchMethodException e) { + } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } @@ -116,7 +116,7 @@ static Class load(String name, byte[] bytes, Class reference } else { clazz = DEFINE_CLASS.invoke(MethodHandles.lookup(), bytes); } - } catch(IllegalAccessException | InvocationTargetException e) { + } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } return (Class) clazz; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 044d688232e8..ee566c503328 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -34,7 +34,7 @@ public static StaticShapeBuilder newBuilder() { return new StaticShapeBuilder(); } - final protected void setFactory(T factory) { + protected final void setFactory(T factory) { if (this.factory != null) { throw new RuntimeException("Attempt to reinitialize the offset of a static property. Was it added to more than one builder?"); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java index b532129884ad..0d9ed24ad9ab 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java @@ -58,7 +58,7 @@ public final class ArrayKlass extends Klass { // This field should be static final, but until we move the static object model we cannot have a // SubstrateVM feature which will allow us to set the right field offsets at image build time. @CompilationFinal // - private static StaticShape ARRAY_SHAPE; + private static StaticShape arrayShape; ArrayKlass(Klass componentType) { super(componentType.getContext(), @@ -79,16 +79,16 @@ public static StaticProperty getArrayProperty() { } public static StaticShape getArrayShape() { - if (ARRAY_SHAPE == null) { + if (arrayShape == null) { initializeArrayShape(); } - return ARRAY_SHAPE; + return arrayShape; } @TruffleBoundary - private synchronized static void initializeArrayShape() { - if (ARRAY_SHAPE == null) { - ARRAY_SHAPE = StaticShape.newBuilder().property(ARRAY_PROPERTY, "array", true).build(StaticObject.class, StaticObjectFactory.class); + private static synchronized void initializeArrayShape() { + if (arrayShape == null) { + arrayShape = StaticShape.newBuilder().property(ARRAY_PROPERTY, "array", true).build(StaticObject.class, StaticObjectFactory.class); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index c1511bfe7b37..53de43fb210d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -474,7 +474,7 @@ public int compare(Klass k1, Klass k2) { // This field should be static final, but until we move the static object model we cannot have a // SubstrateVM feature which will allow us to set the right field offsets at image build time. @CompilationFinal // - private static StaticShape FOREIGN_SHAPE; + private static StaticShape foreignShape; protected Symbol name; protected Symbol type; @@ -614,16 +614,16 @@ public static StaticProperty getForeignProperty() { } public static StaticShape getForeignShape() { - if (FOREIGN_SHAPE == null) { + if (foreignShape == null) { initializeForeignShape(); } - return FOREIGN_SHAPE; + return foreignShape; } @TruffleBoundary - private synchronized static void initializeForeignShape() { - if (FOREIGN_SHAPE == null) { - FOREIGN_SHAPE = StaticShape.newBuilder().property(FOREIGN_PROPERTY, "foreignObject", true).build(StaticObject.class, StaticObjectFactory.class); + private static synchronized void initializeForeignShape() { + if (foreignShape == null) { + foreignShape = StaticShape.newBuilder().property(FOREIGN_PROPERTY, "foreignObject", true).build(StaticObject.class, StaticObjectFactory.class); } } From acb5f7bd93709f477b57c9b7ada71d87e344affb Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 12 Apr 2021 20:18:57 +0200 Subject: [PATCH 174/290] Fix formatting. --- .../espresso/staticobject/ArrayBasedStaticShape.java | 7 ++++--- .../truffle/espresso/impl/LinkedKlassFieldLayout.java | 3 ++- .../src/com/oracle/truffle/espresso/impl/ObjectKlass.java | 2 +- .../nodes/helper/EspressoReferenceArrayStoreNode.java | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java index e27983788829..e03f8b80dbba 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -71,13 +71,14 @@ private static synchronized void initializeShapeChecks() { } } - static ArrayBasedStaticShape create(Class generatedStorageClass, Class generatedFactoryClass, ArrayBasedStaticShape parentShape, Collection extendedProperties, - int byteArrayOffset, int objectArrayOffset, int shapeOffset) { + static ArrayBasedStaticShape create(Class generatedStorageClass, Class generatedFactoryClass, ArrayBasedStaticShape parentShape, + Collection extendedProperties, int byteArrayOffset, int objectArrayOffset, int shapeOffset) { try { ArrayBasedPropertyLayout parentPropertyLayout = parentShape == null ? null : parentShape.getPropertyLayout(); ArrayBasedPropertyLayout propertyLayout = new ArrayBasedPropertyLayout(parentPropertyLayout, extendedProperties, byteArrayOffset, objectArrayOffset, shapeOffset); ArrayBasedStaticShape shape = new ArrayBasedStaticShape<>(parentShape, generatedStorageClass, propertyLayout); - T factory = generatedFactoryClass.cast(generatedFactoryClass.getConstructor(Object.class, int.class, int.class).newInstance(shape, propertyLayout.getPrimitiveArraySize(), propertyLayout.getObjectArraySize())); + T factory = generatedFactoryClass.cast( + generatedFactoryClass.getConstructor(Object.class, int.class, int.class).newInstance(shape, propertyLayout.getPrimitiveArraySize(), propertyLayout.getObjectArraySize())); shape.setFactory(factory); return shape; } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java index b21ec8535732..704976c28cee 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java @@ -81,7 +81,8 @@ final class LinkedKlassFieldLayout { instanceShape = instanceBuilder.build(superKlass.getShape(false)); } else { // The redefining klass is a subshape of the redefined klass. - // This allows shape checks on field accesses using the old StaticProperty instances, but it might not work if the redefined shape has other subshapes. + // This allows shape checks on field accesses using the old StaticProperty + // instances, but it might not work if the redefined shape has other subshapes. instanceShape = instanceBuilder.build(redefinedKlass.getShape(false)); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index 42f95dae88bd..ff07c2f97db8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1099,7 +1099,7 @@ public void redefineClass(ChangePacket packet, List refreshSubClass for (int i = 0; i < superInterfaces.length; i++) { interfaces[i] = superInterfaces[i].getLinkedKlass(); } - LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); + LinkedKlass linkedKlass = LinkedKlass.redefine(parserKlass, getSuperKlass().getLinkedKlass(), interfaces, getLinkedKlass()); // fields if (!change.getOuterFields().isEmpty()) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java index 2bbaab474f49..92e19a52deb9 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java @@ -46,7 +46,7 @@ public EspressoReferenceArrayStoreNode(EspressoContext context) { public void arrayStore(StaticObject value, int index, StaticObject array) { if (StaticObject.isNull(value) || instanceOf(value, ((ArrayKlass) array.getKlass()).getComponentType())) { try { - (array.unwrap())[index] = value; + (array. unwrap())[index] = value; } catch (ArrayIndexOutOfBoundsException e) { enterOutOfBound1(); Meta meta = typeCheck.getMeta(); From 2a6647d3db18045f394b788b09dc7f020f246e53 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 8 Apr 2021 15:13:55 +0200 Subject: [PATCH 175/290] Run awfy benchmarks also with disabled shape checks. --- espresso/ci.jsonnet | 1 + espresso/mx.espresso/mx_espresso_benchmarks.py | 1 + 2 files changed, 2 insertions(+) diff --git a/espresso/ci.jsonnet b/espresso/ci.jsonnet index a43b1bdf3ddb..8c000e4e5936 100644 --- a/espresso/ci.jsonnet +++ b/espresso/ci.jsonnet @@ -36,6 +36,7 @@ // Benchmarks // AWFY peak perf. benchmarks common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g'])+ {name: 'weekly-bench-espresso-jvm-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-jdk8-linux-amd64'}, // AWFY interpreter benchmarks diff --git a/espresso/mx.espresso/mx_espresso_benchmarks.py b/espresso/mx.espresso/mx_espresso_benchmarks.py index 83d8e3460462..43648a2ef1de 100644 --- a/espresso/mx.espresso/mx_espresso_benchmarks.py +++ b/espresso/mx.espresso/mx_espresso_benchmarks.py @@ -133,6 +133,7 @@ def run_with_heap(heap, args, timeout, suppressStderr=True, nonZeroIsFatal=False mx_benchmark.java_vm_registry.add_vm(EspressoVm('multi-tier', ['--experimental-options', '--engine.MultiTier=true']), _suite) mx_benchmark.java_vm_registry.add_vm(EspressoVm('multi-tier-inline-accessors', ['--experimental-options', '--engine.MultiTier', '--java.InlineFieldAccessors']), _suite) mx_benchmark.java_vm_registry.add_vm(EspressoVm('no-inlining', ['--experimental-options', '--engine.Inlining=false']), _suite) +mx_benchmark.java_vm_registry.add_vm(EspressoVm('no-shape-checks', ['--vm.Dcom.oracle.truffle.espresso.staticobject.DisableShapeChecks=true']), _suite) mx_benchmark.java_vm_registry.add_vm(EspressoMinHeapVm(0, 0, 64, 'infinite-overhead', []), _suite) mx_benchmark.java_vm_registry.add_vm(EspressoMinHeapVm(1.5, 0, 2048, '1.5-overhead', []), _suite) From 8b7759e2cbbf0b57d2351743d4a486f7d05d26f7 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 8 Apr 2021 18:28:51 +0200 Subject: [PATCH 176/290] Run native awfy benchmarks also with disabled shape checks. --- espresso/ci.jsonnet | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/espresso/ci.jsonnet b/espresso/ci.jsonnet index 8c000e4e5936..c4d2355ef2fa 100644 --- a/espresso/ci.jsonnet +++ b/espresso/ci.jsonnet @@ -35,13 +35,14 @@ builds: common.builds + [ // Benchmarks // AWFY peak perf. benchmarks - common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g'])+ {name: 'weekly-bench-espresso-jvm-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, // AWFY interpreter benchmarks - common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('jvm-ce', 'awfy:*') + {name: 'weekly-bench-espresso-jvm-ce-awfy_interpreter-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('native-ce', 'awfy:*') + {name: 'weekly-bench-espresso-native-ce-awfy_interpreter-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('jvm-ce', 'awfy:*') + {name: 'weekly-bench-espresso-jvm-ce-awfy_interpreter-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('native-ce', 'awfy:*') + {name: 'weekly-bench-espresso-native-ce-awfy_interpreter-jdk8-linux-amd64'}, // Scala DaCapo warmup benchmarks common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('jvm-ce', 'single-tier', extra_args=['--vm.XX:ReservedCodeCacheSize=1g']) + {name: 'weekly-bench-espresso-jvm-ce-scala_dacapo_warmup-single_tier-jdk8-linux-amd64'}, From c9d4f3291762edafbd5b5ae8bd0fcc6573dd252c Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 13 Apr 2021 19:07:13 +0200 Subject: [PATCH 177/290] Allocate one shape generator per (storage; factory) pair. --- .../ArrayBasedShapeGenerator.java | 62 ++++++------------- .../espresso/staticobject/ShapeGenerator.java | 17 ++--- .../staticobject/StaticShapeBuilder.java | 12 ++-- 3 files changed, 28 insertions(+), 63 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index aec43463328e..9cde992ecca9 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.impl.asm.ClassVisitor; import com.oracle.truffle.api.impl.asm.ClassWriter; @@ -66,9 +67,9 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { private static final int UNINITIALIZED_NATIVE_OFFSET = -1; private static final ConcurrentHashMap, Class>, ArrayBasedShapeGenerator> generatorCache = new ConcurrentHashMap<>(); - private final int byteArrayOffset; - private final int objectArrayOffset; - private final int shapeOffset; + @CompilationFinal private int byteArrayOffset; + @CompilationFinal private int objectArrayOffset; + @CompilationFinal private int shapeOffset; static { if (TruffleOptions.AOT) { @@ -79,11 +80,10 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { Class defaultStorageFactoryInterface = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject$StaticObjectFactory"); Class defaultStorageClass = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObject"); Class defaultFactoryClass = Class.forName("com.oracle.truffle.espresso.runtime.StaticObject$DefaultArrayBasedStaticObjectFactory"); - Collection storageProperties = generateStorageProperties(); // The offsets of the byte and object arrays cannot be computed at image build time. // They would refer to a Java object, not to a Native object. - ArrayBasedShapeGenerator sg = new ArrayBasedShapeGenerator<>(defaultStorageClass, defaultFactoryClass, storageProperties, UNINITIALIZED_NATIVE_OFFSET, - UNINITIALIZED_NATIVE_OFFSET, UNINITIALIZED_NATIVE_OFFSET); + ArrayBasedShapeGenerator sg = new ArrayBasedShapeGenerator<>(defaultStorageClass, defaultFactoryClass, UNINITIALIZED_NATIVE_OFFSET, UNINITIALIZED_NATIVE_OFFSET, + UNINITIALIZED_NATIVE_OFFSET); generatorCache.putIfAbsent(Pair.create(defaultStorageSuperClass, defaultStorageFactoryInterface), sg); } catch (ClassNotFoundException e) { throw new RuntimeException(e); @@ -91,67 +91,41 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { } } - private ArrayBasedShapeGenerator(ArrayBasedShapeGenerator rootSG, Collection extendedProperties, StaticShape parentShape) { - this( - rootSG.generatedStorageClass, - rootSG.generatedFactoryClass, - extendedProperties, - parentShape, - TruffleOptions.AOT && rootSG.byteArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "primitive") : rootSG.byteArrayOffset, - TruffleOptions.AOT && rootSG.objectArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "object") : rootSG.objectArrayOffset, - TruffleOptions.AOT && rootSG.objectArrayOffset == UNINITIALIZED_NATIVE_OFFSET ? getObjectFieldOffset(rootSG.generatedStorageClass, "shape") : rootSG.shapeOffset); - } - - private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties) { + private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass) { this( generatedStorageClass, generatedFactoryClass, - extendedProperties, - null, getObjectFieldOffset(generatedStorageClass, "primitive"), getObjectFieldOffset(generatedStorageClass, "object"), getObjectFieldOffset(generatedStorageClass, "shape")); } - private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, int byteArrayOffset, - int objectArrayOffset, int shapeOffset) { - this(generatedStorageClass, generatedFactoryClass, extendedProperties, null, byteArrayOffset, objectArrayOffset, shapeOffset); - } - - private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, StaticShape parentShape, - int byteArrayOffset, int objectArrayOffset, int shapeOffset) { - super(generatedStorageClass, generatedFactoryClass, extendedProperties, parentShape); + private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, int byteArrayOffset, int objectArrayOffset, int shapeOffset) { + super(generatedStorageClass, generatedFactoryClass); this.byteArrayOffset = byteArrayOffset; this.objectArrayOffset = objectArrayOffset; this.shapeOffset = shapeOffset; } @SuppressWarnings("unchecked") - static ArrayBasedShapeGenerator getShapeGenerator(StaticShape parentShape, Collection extendedProperties) { - Class parentStorageSuperClass = parentShape.getStorageClass().getSuperclass(); - Class parentStorageFactoryInterface = parentShape.getFactoryInterface(); - ArrayBasedShapeGenerator rootSG = (ArrayBasedShapeGenerator) generatorCache.get(Pair.create(parentStorageSuperClass, parentStorageFactoryInterface)); - assert rootSG != null; - return new ArrayBasedShapeGenerator<>(rootSG, extendedProperties, parentShape); - } - - @SuppressWarnings("unchecked") - static ArrayBasedShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface, Collection extendedProperties) { + static ArrayBasedShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface) { Pair, Class> pair = Pair.create(storageSuperClass, storageFactoryInterface); ArrayBasedShapeGenerator sg = (ArrayBasedShapeGenerator) generatorCache.get(pair); if (sg == null) { assert !TruffleOptions.AOT; Class generatedStorageClass = generateStorage(storageSuperClass); Class generatedFactoryClass = generateFactory(generatedStorageClass, storageFactoryInterface); - sg = new ArrayBasedShapeGenerator<>(generatedStorageClass, generatedFactoryClass, extendedProperties); + sg = new ArrayBasedShapeGenerator<>(generatedStorageClass, generatedFactoryClass); ArrayBasedShapeGenerator prevSg = (ArrayBasedShapeGenerator) generatorCache.putIfAbsent(pair, sg); - if (prevSg == null) { - return sg; - } else { + if (prevSg != null) { sg = prevSg; } + } else if (TruffleOptions.AOT && sg.byteArrayOffset == UNINITIALIZED_NATIVE_OFFSET) { + sg.byteArrayOffset = getObjectFieldOffset(sg.generatedStorageClass, "primitive"); + sg.objectArrayOffset = getObjectFieldOffset(sg.generatedStorageClass, "object"); + sg.shapeOffset = getObjectFieldOffset(sg.generatedStorageClass, "shape"); } - return new ArrayBasedShapeGenerator<>(sg, extendedProperties, null); + return sg; } private static int getObjectFieldOffset(Class c, String fieldName) { @@ -163,7 +137,7 @@ private static int getObjectFieldOffset(Class c, String fieldName) { } @Override - StaticShape generateShape() { + StaticShape generateShape(StaticShape parentShape, Collection extendedProperties) { return ArrayBasedStaticShape.create(generatedStorageClass, generatedFactoryClass, (ArrayBasedStaticShape) parentShape, extendedProperties, byteArrayOffset, objectArrayOffset, shapeOffset); } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 25b999984220..1268925de593 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -49,8 +49,6 @@ abstract class ShapeGenerator { protected final Class generatedStorageClass; protected final Class generatedFactoryClass; - protected final Collection extendedProperties; - protected final StaticShape parentShape; static { String value = System.getProperty("java.specification.version"); @@ -69,23 +67,16 @@ abstract class ShapeGenerator { } } - ShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass, Collection extendedProperties, StaticShape parentShape) { + ShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass) { this.generatedStorageClass = generatedStorageClass; this.generatedFactoryClass = generatedFactoryClass; - this.extendedProperties = extendedProperties; - this.parentShape = parentShape; } - abstract StaticShape generateShape(); + abstract StaticShape generateShape(StaticShape parentShape, Collection extendedProperties); @SuppressWarnings("unchecked") - static ShapeGenerator getShapeGenerator(StaticShape parentShape, Collection extendedProperties) { - return ArrayBasedShapeGenerator.getShapeGenerator(parentShape, extendedProperties); - } - - @SuppressWarnings("unchecked") - static ShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface, Collection extendedProperties) { - return ArrayBasedShapeGenerator.getShapeGenerator(storageSuperClass, storageFactoryInterface, extendedProperties); + static ShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface) { + return ArrayBasedShapeGenerator.getShapeGenerator(storageSuperClass, storageFactoryInterface); } static String generateStorageName() { diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java index 1500f2ee3fc4..3df7c3b3751e 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java @@ -50,19 +50,19 @@ public StaticShape build() { public StaticShape build(StaticShape parentShape) { Objects.requireNonNull(parentShape); - ShapeGenerator sg = ShapeGenerator.getShapeGenerator(parentShape, extendedProperties.values()); - return build(sg); + ShapeGenerator sg = ShapeGenerator.getShapeGenerator(parentShape.getStorageClass().getSuperclass(), parentShape.getFactoryInterface()); + return build(sg, parentShape); } public StaticShape build(Class superClass, Class factoryInterface) { validate(factoryInterface, superClass); - ShapeGenerator sg = ShapeGenerator.getShapeGenerator(superClass, factoryInterface, extendedProperties.values()); - return build(sg); + ShapeGenerator sg = ShapeGenerator.getShapeGenerator(superClass, factoryInterface); + return build(sg, null); } - private StaticShape build(ShapeGenerator sg) { - StaticShape shape = sg.generateShape(); + private StaticShape build(ShapeGenerator sg, StaticShape parentShape) { + StaticShape shape = sg.generateShape(parentShape, extendedProperties.values()); for (ExtendedProperty extendedProperty : extendedProperties.values()) { extendedProperty.property.initShape(shape); } From 65fc4d91af3dfacc377617fe5ba601536bbb3faa Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 14 Apr 2021 19:35:10 +0200 Subject: [PATCH 178/290] Revert changes to support class redefinition. The subshape replicated fields in the redefined shape. It was enough to pass the gate but was not correct. --- .../truffle/espresso/impl/LinkedKlass.java | 28 +++++++++++++++---- .../espresso/impl/LinkedKlassFieldLayout.java | 11 ++------ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java index ab86133cc924..d3c1018c7168 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlass.java @@ -100,11 +100,7 @@ private LinkedKlass(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass } public static LinkedKlass create(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces) { - return redefine(parserKlass, superKlass, interfaces, null); - } - - public static LinkedKlass redefine(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces, LinkedKlass redefinedKlass) { - LinkedKlassFieldLayout fieldLayout = new LinkedKlassFieldLayout(parserKlass, superKlass, redefinedKlass); + LinkedKlassFieldLayout fieldLayout = new LinkedKlassFieldLayout(parserKlass, superKlass); return new LinkedKlass( parserKlass, superKlass, @@ -116,6 +112,28 @@ public static LinkedKlass redefine(ParserKlass parserKlass, LinkedKlass superKla fieldLayout.fieldTableLength); } + public static LinkedKlass redefine(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass[] interfaces, LinkedKlass redefinedKlass) { + // On class redefinition we need to re-use the old shape. + // If we don't do it, shape checks on field accesses fail because `Field` instances in + // `ObjectKlass.fieldTable` hold references to the old shape, which does not match the shape + // of the new object instances. + // If we work around this issue by patching the `ObjectKlass.fieldTable` on class + // redefinition, these new `Field` instances cannot be used to access object instances with + // the old shape. + // An option would be to create a new shape that stems from the redefined one and contains + // only the new fields. + // However, this would not work if the redefined shape has subtypes. + return new LinkedKlass( + parserKlass, + superKlass, + interfaces, + redefinedKlass.instanceShape, + redefinedKlass.staticShape, + redefinedKlass.instanceFields, + redefinedKlass.staticFields, + redefinedKlass.fieldTableLength); + } + int getFlags() { int flags = parserKlass.getFlags(); if (hasFinalizer) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java index 704976c28cee..3aab7a9dd5eb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java @@ -45,7 +45,7 @@ final class LinkedKlassFieldLayout { final int fieldTableLength; - LinkedKlassFieldLayout(ParserKlass parserKlass, LinkedKlass superKlass, LinkedKlass redefinedKlass) { + LinkedKlassFieldLayout(ParserKlass parserKlass, LinkedKlass superKlass) { StaticShapeBuilder instanceBuilder = StaticShape.newBuilder(); StaticShapeBuilder staticBuilder = StaticShape.newBuilder(); @@ -77,14 +77,7 @@ final class LinkedKlassFieldLayout { if (superKlass == null) { instanceShape = instanceBuilder.build(StaticObject.class, StaticObjectFactory.class); } else { - if (redefinedKlass == null) { - instanceShape = instanceBuilder.build(superKlass.getShape(false)); - } else { - // The redefining klass is a subshape of the redefined klass. - // This allows shape checks on field accesses using the old StaticProperty - // instances, but it might not work if the redefined shape has other subshapes. - instanceShape = instanceBuilder.build(redefinedKlass.getShape(false)); - } + instanceShape = instanceBuilder.build(superKlass.getShape(false)); } staticShape = staticBuilder.build(StaticObject.class, StaticObjectFactory.class); fieldTableLength = nextInstanceFieldSlot; From 5e2b47d1730dbf86531f9d5324188973b783c1c3 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 15 Apr 2021 12:31:45 +0200 Subject: [PATCH 179/290] Use TypeCheckNode. --- .../espresso/nodes/helper/EspressoReferenceArrayStoreNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java index 92e19a52deb9..f960a0da4f6c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java @@ -44,7 +44,7 @@ public EspressoReferenceArrayStoreNode(EspressoContext context) { } public void arrayStore(StaticObject value, int index, StaticObject array) { - if (StaticObject.isNull(value) || instanceOf(value, ((ArrayKlass) array.getKlass()).getComponentType())) { + if (StaticObject.isNull(value) || typeCheck.executeTypeCheck(((ArrayKlass) array.getKlass()).getComponentType(), value.getKlass())) { try { (array. unwrap())[index] = value; } catch (ArrayIndexOutOfBoundsException e) { From 3677a9347218f399ad7ecfad95e40897757630c3 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 15 Apr 2021 17:47:37 +0200 Subject: [PATCH 180/290] Remove unused import. --- .../espresso/nodes/helper/EspressoReferenceArrayStoreNode.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java index f960a0da4f6c..b603bcf0990a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java @@ -31,8 +31,6 @@ import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.StaticObject; -import static com.oracle.truffle.espresso.vm.InterpreterToVM.instanceOf; - public class EspressoReferenceArrayStoreNode extends Node { @Child TypeCheckNode typeCheck; @CompilationFinal boolean noOutOfBoundEx1 = true; From 3e56df02638fdd71cb3d86cff26d212904949ee0 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 15 Apr 2021 17:48:14 +0200 Subject: [PATCH 181/290] Improve the internal API of ShapeGenerator. --- .../oracle/truffle/espresso/staticobject/ShapeGenerator.java | 5 ++++- .../truffle/espresso/staticobject/StaticShapeBuilder.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 1268925de593..bee5241123ab 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -74,7 +74,10 @@ abstract class ShapeGenerator { abstract StaticShape generateShape(StaticShape parentShape, Collection extendedProperties); - @SuppressWarnings("unchecked") + static ShapeGenerator getShapeGenerator(StaticShape parentShape) { + return getShapeGenerator(parentShape.getStorageClass().getSuperclass(), parentShape.getFactoryInterface()); + } + static ShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface) { return ArrayBasedShapeGenerator.getShapeGenerator(storageSuperClass, storageFactoryInterface); } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java index 3df7c3b3751e..4bf695c7e510 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java @@ -50,7 +50,7 @@ public StaticShape build() { public StaticShape build(StaticShape parentShape) { Objects.requireNonNull(parentShape); - ShapeGenerator sg = ShapeGenerator.getShapeGenerator(parentShape.getStorageClass().getSuperclass(), parentShape.getFactoryInterface()); + ShapeGenerator sg = ShapeGenerator.getShapeGenerator(parentShape); return build(sg, parentShape); } From a66a2d62119343f79f9139b81a58b207b682d2e8 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Thu, 15 Apr 2021 20:49:46 +0200 Subject: [PATCH 182/290] Do not allow adding the same static property more than once. --- .../truffle/espresso/staticobject/StaticShapeBuilder.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java index 4bf695c7e510..fd1957bae002 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java @@ -37,6 +37,11 @@ public StaticShapeBuilder property(StaticProperty property, String name, boolean if (extendedProperties.containsKey(name)) { throw new IllegalArgumentException("This builder already contains a property named '" + name + "'"); } + for (ExtendedProperty extendedProperty : extendedProperties.values()) { + if (extendedProperty.property.equals(property)) { + throw new IllegalArgumentException("This builder already contains this property"); + } + } extendedProperties.put(name, new ExtendedProperty(property, name, isFinal)); return this; } From ddf970749ec22aa0cd6e46fe0804b899f96a46b1 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 4 Dec 2020 12:54:52 +0100 Subject: [PATCH 183/290] Add some tests for the static object model. --- espresso/mx.espresso/suite.py | 24 ++ .../test/BuilderPropertyTest.java | 165 ++++++++++++++ .../staticobject/test/InheritanceTest.java | 102 +++++++++ .../staticobject/test/PropertyAccessTest.java | 210 ++++++++++++++++++ .../staticobject/test/StorageLayout.java | 29 +++ 5 files changed, 530 insertions(+) create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index d87351bb2245..2d7068314b8e 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -110,6 +110,17 @@ "checkstyle": "com.oracle.truffle.espresso", }, + "com.oracle.truffle.espresso.staticobject.test": { + "subDir" : "src", + "sourceDirs" : ["src"], + "dependencies" : [ + "com.oracle.truffle.espresso.staticobject", + "mx:JUNIT" + ], + "javaCompliance": "1.8+", + "checkstyle": "com.oracle.truffle.espresso", + }, + "com.oracle.truffle.espresso.processor": { "subDir": "src", "sourceDirs": ["src"], @@ -277,6 +288,19 @@ }, }, + "ESPRESSO_TESTS": { + "subDir": "src", + "dependencies": [ + "com.oracle.truffle.espresso.staticobject.test", + ], + "distDependencies": [ + "espresso:ESPRESSO", + ], + "exclude": [ + "mx:JUNIT", + ], + }, + "ESPRESSO_LAUNCHER": { "subDir": "src", "dependencies": [ diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java new file mode 100644 index 000000000000..dcb2977bf024 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject.test; + +import com.oracle.truffle.espresso.staticobject.DefaultStaticObject; +import com.oracle.truffle.espresso.staticobject.DefaultStaticObject.DefaultStaticObjectFactory; +import com.oracle.truffle.espresso.staticobject.StaticProperty; +import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; +import com.oracle.truffle.espresso.staticobject.StaticShape; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class BuilderPropertyTest { + @Test + public void sameBuilderSameProperty() { + StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticProperty property = new StaticProperty(StaticPropertyKind.Int); + builder.property(property, "p1", false); + try { + // You cannot add the same property twice + builder.property(property, "p2", false); + Assert.fail(); + } catch (IllegalArgumentException e) { + Assert.assertEquals("This builder already contains this property", e.getMessage()); + } + } + + @Test + public void sameBuilderSameName() throws IllegalArgumentException { + StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticProperty p1 = new StaticProperty(StaticPropertyKind.Int); + StaticProperty p2 = new StaticProperty(StaticPropertyKind.Int); + builder.property(p1, "p1", false); + try { + // You cannot add two properties with the same name + builder.property(p2, "p1", false); + Assert.fail(); + } catch (IllegalArgumentException e) { + Assert.assertEquals("This builder already contains a property named 'p1'", e.getMessage()); + } + } + + @Test + public void differentBuildersSameProperty() { + StaticShapeBuilder b1 = StaticShape.newBuilder(); + StaticShapeBuilder b2 = StaticShape.newBuilder(); + StaticProperty property = new StaticProperty(StaticPropertyKind.Int); + b1.property(property, "p1", false); + b2.property(property, "p2", false); + b1.build(); + try { + // You cannot build shapes that share properties + b2.build(); + Assert.fail(); + } catch (RuntimeException e) { + Assert.assertEquals("Attempt to reinitialize the offset of a static property. Was it added to more than one builder?", e.getMessage()); + } + } + + @Test + public void propertyName() throws NoSuchFieldException { + Assume.assumeFalse(StorageLayout.ARRAY_BASED); + + StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticProperty property = new StaticProperty(StaticPropertyKind.Int); + String propertyName = "p1"; + builder.property(property, propertyName, false); + StaticShape shape = builder.build(); + DefaultStaticObject object = shape.getFactory().create(); + Field field = object.getClass().getField(propertyName); + Assert.assertEquals(propertyName, field.getName()); + } + + @Test + public void propertyFinal() throws NoSuchFieldException { + Assume.assumeFalse(StorageLayout.ARRAY_BASED); + + StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticProperty p1 = new StaticProperty(StaticPropertyKind.Int); + StaticProperty p2 = new StaticProperty(StaticPropertyKind.Int); + String p1Name = "p1"; + String p2Name = "p2"; + builder.property(p1, p1Name, true); + builder.property(p2, p2Name, false); + StaticShape shape = builder.build(); + DefaultStaticObject object = shape.getFactory().create(); + Field f1 = object.getClass().getField(p1Name); + Field f2 = object.getClass().getField(p2Name); + Assert.assertTrue(Modifier.isFinal(f1.getModifiers())); + Assert.assertFalse(Modifier.isFinal(f2.getModifiers())); + } + + @Test + public void propertyKind() throws NoSuchFieldException { + Assume.assumeFalse(StorageLayout.ARRAY_BASED); + + StaticShapeBuilder builder = StaticShape.newBuilder(); + for (StaticPropertyKind kind : StaticPropertyKind.values()) { + builder.property(new StaticProperty(kind), kind.name(), false); + } + StaticShape shape = builder.build(); + DefaultStaticObject object = shape.getFactory().create(); + for (StaticPropertyKind kind : StaticPropertyKind.values()) { + Class expectedType; + switch (kind) { + case Boolean: + expectedType = boolean.class; + break; + case Byte: + expectedType = byte.class; + break; + case Char: + expectedType = char.class; + break; + case Double: + expectedType = double.class; + break; + case Float: + expectedType = float.class; + break; + case Int: + expectedType = int.class; + break; + case Long: + expectedType = long.class; + break; + case Object: + expectedType = Object.class; + break; + case Short: + expectedType = short.class; + break; + default: + expectedType = null; + Assert.fail("Unexpected type: " + kind); + } + Assert.assertEquals(expectedType, object.getClass().getField(kind.name()).getType()); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java new file mode 100644 index 000000000000..d59f01e6245f --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject.test; + +import com.oracle.truffle.espresso.staticobject.DefaultStaticObject; +import com.oracle.truffle.espresso.staticobject.DefaultStaticObject.DefaultStaticObjectFactory; +import com.oracle.truffle.espresso.staticobject.StaticProperty; +import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; +import com.oracle.truffle.espresso.staticobject.StaticShape; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; + +public class InheritanceTest { + public static class CustomStaticObject { + public byte field1; + public boolean field2; + } + + public interface CustomStaticObjectFactory { + CustomStaticObject create(); + } + + @Test + public void baseClassInheritance() throws NoSuchFieldException { + StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticProperty property = new StaticProperty(StaticPropertyKind.Int); + builder.property(property, "field1", false); + StaticShape shape = builder.build(CustomStaticObject.class, CustomStaticObjectFactory.class); + CustomStaticObject object = shape.getFactory().create(); + + // Set to the field declared in the super class + object.field1 = 42; + // Set to the field declared in the generated class + property.setInt(object, 24); + // Get the value of the field declared in the super class + Assert.assertEquals(42, object.field1); + // Get the value of the field declared in the generated class + Assert.assertEquals(24, property.getInt(object)); + + Assume.assumeFalse(StorageLayout.ARRAY_BASED); + // `CustomStaticObject.field1` is shadowed + Assert.assertEquals(int.class, object.getClass().getField("field1").getType()); + // `CustomStaticObject.field2` is visible + Assert.assertEquals(boolean.class, object.getClass().getField("field2").getType()); + } + + @Test + public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessException { + StaticShapeBuilder b1 = StaticShape.newBuilder(); + StaticProperty s1p1 = new StaticProperty(StaticPropertyKind.Int); + StaticProperty s1p2 = new StaticProperty(StaticPropertyKind.Int); + b1.property(s1p1, "field1", false); + b1.property(s1p2, "field2", false); + // StaticShape s1 declares 2 properties: s1p1 and s1p2 + StaticShape s1 = b1.build(); + + StaticShapeBuilder b2 = StaticShape.newBuilder(); + StaticProperty s2p1 = new StaticProperty(StaticPropertyKind.Int); + b2.property(s2p1, "field1", false); + // StaticShape s2: + // 1. extends s1 + // 2. declares one property: s2p1 + // 3. inherits one property from s1: s1p2 + StaticShape s2 = b2.build(s1); + DefaultStaticObject object = s2.getFactory().create(); + + s1p1.setInt(object, 1); + Assert.assertEquals(1, s1p1.getInt(object)); + Assert.assertEquals(0, s2p1.getInt(object)); + s1p2.setInt(object, 2); + Assert.assertEquals(2, s1p2.getInt(object)); + s2p1.setInt(object, 3); + // s1p1 accesses the field declared in + Assert.assertEquals(1, s1p1.getInt(object)); + Assert.assertEquals(3, s2p1.getInt(object)); + + Assume.assumeFalse(StorageLayout.ARRAY_BASED); + Assert.assertEquals(3, object.getClass().getField("field1").getInt(object)); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java new file mode 100644 index 000000000000..e5ebf02eee55 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject.test; + +import com.oracle.truffle.espresso.staticobject.DefaultStaticObject; +import com.oracle.truffle.espresso.staticobject.StaticProperty; +import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; +import com.oracle.truffle.espresso.staticobject.StaticShape; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +@RunWith(Theories.class) +public class PropertyAccessTest { + @DataPoints // + public static TestDescriptor[] descriptors; + + static class TestDescriptor { + final StaticPropertyKind kind; + final Object testValue; + final Object defaultValue; + final PropertyGetter getter; + final PropertySetter setter; + + TestDescriptor(StaticPropertyKind kind, Object testValue, Object defaultValue, PropertyGetter getter, PropertySetter setter) { + this.kind = kind; + this.testValue = testValue; + this.defaultValue = defaultValue; + this.getter = getter; + this.setter = setter; + } + + @Override + public String toString() { + return kind.name(); + } + } + + @FunctionalInterface + interface PropertyGetter { + Object get(StaticProperty property, DefaultStaticObject receiver); + } + + @FunctionalInterface + interface PropertySetter { + void set(StaticProperty property, DefaultStaticObject receiver, Object value); + } + + @BeforeClass + public static void init() { + descriptors = new TestDescriptor[StaticPropertyKind.values().length]; + + for (StaticPropertyKind kind : StaticPropertyKind.values()) { + int i = kind.ordinal(); + PropertyGetter getter; + PropertySetter setter; + + switch (kind) { + case Boolean: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Boolean, + true, + false, + (p, obj) -> p.getBoolean(obj), + (p, obj, val) -> p.setBoolean(obj, (boolean) val)); + break; + case Byte: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Byte, + (byte) 42, + (byte) 0, + (p, obj) -> p.getByte(obj), + (p, obj, val) -> p.setByte(obj, (byte) val)); + break; + case Char: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Char, + (char) 42, + (char) 0, + (p, obj) -> p.getChar(obj), + (p, obj, val) -> p.setChar(obj, (char) val)); + break; + case Double: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Double, + 42D, + 0D, + (p, obj) -> p.getDouble(obj), + (p, obj, val) -> p.setDouble(obj, (double) val)); + break; + case Float: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Float, + 42F, + 0F, + (p, obj) -> p.getFloat(obj), + (p, obj, val) -> p.setFloat(obj, (float) val)); + break; + case Int: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Int, + 42, + 0, + (p, obj) -> p.getInt(obj), + (p, obj, val) -> p.setInt(obj, (int) val)); + break; + case Long: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Long, + 42L, + 0L, + (p, obj) -> p.getLong(obj), + (p, obj, val) -> p.setLong(obj, (long) val)); + break; + case Short: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Short, + (short) 42, + (short) 0, + (p, obj) -> p.getShort(obj), + (p, obj, val) -> p.setShort(obj, (short) val)); + break; + case Object: + descriptors[i] = new TestDescriptor( + StaticPropertyKind.Object, + new Object(), + null, + (p, obj) -> p.getObject(obj), + (p, obj, val) -> p.setObject(obj, val)); + break; + default: + Assert.fail(); + } + } + } + + @Theory + public void correctAccessors(TestDescriptor descriptor) { + StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticProperty property = new StaticProperty(descriptor.kind); + builder.property(property, "property", false); + StaticShape shape = builder.build(); + DefaultStaticObject object = shape.getFactory().create(); + + // Check the default value + Object actualValue = descriptor.getter.get(property, object); + Assert.assertEquals(descriptor.defaultValue, actualValue); + // Check property accesses + descriptor.setter.set(property, object, descriptor.testValue); + actualValue = descriptor.getter.get(property, object); + Assert.assertEquals(descriptor.testValue, actualValue); + } + + @Theory + public void wrongAccessors(TestDescriptor expectedDescriptor, TestDescriptor actualDescriptor) { + Assume.assumeFalse(expectedDescriptor.equals(actualDescriptor)); + + StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticProperty property = new StaticProperty(expectedDescriptor.kind); + builder.property(property, "property", false); + StaticShape shape = builder.build(); + DefaultStaticObject object = shape.getFactory().create(); + + // Check that wrong getters throw exceptions + String expectedExceptionMessage = "Static property of '" + expectedDescriptor.kind.name() + "' kind cannot be accessed as '" + actualDescriptor.kind + "'"; + try { + actualDescriptor.getter.get(property, object); + Assert.fail(); + } catch (RuntimeException e) { + Assert.assertEquals(expectedExceptionMessage, e.getMessage()); + } + try { + actualDescriptor.setter.set(property, object, actualDescriptor.testValue); + Assert.fail(); + } catch (RuntimeException e) { + Assert.assertEquals(expectedExceptionMessage, e.getMessage()); + } + } + + @Test + public void dummy() { + // to make sure this file is recognized as a test + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java new file mode 100644 index 000000000000..5785ef93761a --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject.test; + +import java.lang.reflect.Field; + +class StorageLayout { + static final boolean ARRAY_BASED = true; +} From 416ce1403c20d3a072505bea7803f2d299da5e61 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 16 Apr 2021 11:29:17 +0200 Subject: [PATCH 184/290] Remove unused fields and imports. --- .../truffle/espresso/staticobject/test/PropertyAccessTest.java | 3 --- .../truffle/espresso/staticobject/test/StorageLayout.java | 2 -- 2 files changed, 5 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index e5ebf02eee55..065249647a33 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -78,9 +78,6 @@ public static void init() { for (StaticPropertyKind kind : StaticPropertyKind.values()) { int i = kind.ordinal(); - PropertyGetter getter; - PropertySetter setter; - switch (kind) { case Boolean: descriptors[i] = new TestDescriptor( diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java index 5785ef93761a..e44ff2837670 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java @@ -22,8 +22,6 @@ */ package com.oracle.truffle.espresso.staticobject.test; -import java.lang.reflect.Field; - class StorageLayout { static final boolean ARRAY_BASED = true; } From fc28d043e31f03feab4aade3a0ad3e1ff30e2b4b Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 16 Apr 2021 12:16:41 +0200 Subject: [PATCH 185/290] Move fields to store generated classes from `ShapeGenerator` to `ArrayBasedShapeGenerator`. --- .../espresso/staticobject/ArrayBasedShapeGenerator.java | 6 +++++- .../truffle/espresso/staticobject/ShapeGenerator.java | 8 -------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 9cde992ecca9..213e901db089 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -67,6 +67,9 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { private static final int UNINITIALIZED_NATIVE_OFFSET = -1; private static final ConcurrentHashMap, Class>, ArrayBasedShapeGenerator> generatorCache = new ConcurrentHashMap<>(); + private final Class generatedStorageClass; + private final Class generatedFactoryClass; + @CompilationFinal private int byteArrayOffset; @CompilationFinal private int objectArrayOffset; @CompilationFinal private int shapeOffset; @@ -101,7 +104,8 @@ private ArrayBasedShapeGenerator(Class generatedStorageClass, Class generatedStorageClass, Class generatedFactoryClass, int byteArrayOffset, int objectArrayOffset, int shapeOffset) { - super(generatedStorageClass, generatedFactoryClass); + this.generatedStorageClass = generatedStorageClass; + this.generatedFactoryClass = generatedFactoryClass; this.byteArrayOffset = byteArrayOffset; this.objectArrayOffset = objectArrayOffset; this.shapeOffset = shapeOffset; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index bee5241123ab..5b231b8b4d1a 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -47,9 +47,6 @@ abstract class ShapeGenerator { private static final int JAVA_SPEC_VERSION; private static final Method DEFINE_CLASS; - protected final Class generatedStorageClass; - protected final Class generatedFactoryClass; - static { String value = System.getProperty("java.specification.version"); if (value.startsWith("1.")) { @@ -67,11 +64,6 @@ abstract class ShapeGenerator { } } - ShapeGenerator(Class generatedStorageClass, Class generatedFactoryClass) { - this.generatedStorageClass = generatedStorageClass; - this.generatedFactoryClass = generatedFactoryClass; - } - abstract StaticShape generateShape(StaticShape parentShape, Collection extendedProperties); static ShapeGenerator getShapeGenerator(StaticShape parentShape) { From 1dbb29e230fd9c1cd3a2e0b3245f09a5aea96618 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 16 Apr 2021 12:00:57 +0200 Subject: [PATCH 186/290] Add the field-based storage strategy. --- .../staticobject/ArrayBasedStaticShape.java | 16 -- .../FieldBasedShapeGenerator.java | 161 ++++++++++++++++++ .../staticobject/FieldBasedStaticShape.java | 45 +++++ .../espresso/staticobject/ShapeGenerator.java | 11 +- .../espresso/staticobject/StaticShape.java | 19 +++ 5 files changed, 234 insertions(+), 18 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedStaticShape.java diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java index e03f8b80dbba..4f7145015fe5 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -26,7 +26,6 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; @@ -36,7 +35,6 @@ import static com.oracle.truffle.espresso.staticobject.StaticPropertyKind.N_PRIMITIVES; final class ArrayBasedStaticShape extends StaticShape { - private static final Unsafe UNSAFE = getUnsafe(); @CompilationFinal // private static Boolean disableShapeChecks; @CompilationFinal(dimensions = 1) // @@ -107,20 +105,6 @@ private ArrayBasedPropertyLayout getPropertyLayout() { return propertyLayout; } - private static Unsafe getUnsafe() { - try { - return Unsafe.getUnsafe(); - } catch (SecurityException e) { - } - try { - Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeInstance.setAccessible(true); - return (Unsafe) theUnsafeInstance.get(Unsafe.class); - } catch (Exception e) { - throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); - } - } - /** *

    * Creates a layout for the primitive fields of a given class, and assigns to each field the raw diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java new file mode 100644 index 000000000000..f300fd7e8781 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +import com.oracle.truffle.api.impl.asm.ClassVisitor; +import com.oracle.truffle.api.impl.asm.ClassWriter; +import com.oracle.truffle.api.impl.asm.MethodVisitor; +import com.oracle.truffle.api.impl.asm.Type; +import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Collection; + +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_FINAL; +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_PUBLIC; +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_SUPER; +import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_SYNTHETIC; +import static com.oracle.truffle.api.impl.asm.Opcodes.ALOAD; +import static com.oracle.truffle.api.impl.asm.Opcodes.ARETURN; +import static com.oracle.truffle.api.impl.asm.Opcodes.DUP; +import static com.oracle.truffle.api.impl.asm.Opcodes.ILOAD; +import static com.oracle.truffle.api.impl.asm.Opcodes.INVOKESPECIAL; +import static com.oracle.truffle.api.impl.asm.Opcodes.NEW; +import static com.oracle.truffle.api.impl.asm.Opcodes.RETURN; +import static com.oracle.truffle.api.impl.asm.Opcodes.V1_8; + +final class FieldBasedShapeGenerator extends ShapeGenerator { + private final Class storageSuperClass; + private final Class storageFactoryInterface; + + private FieldBasedShapeGenerator(Class storageSuperClass, Class storageFactoryInterface) { + this.storageSuperClass = storageSuperClass; + this.storageFactoryInterface = storageFactoryInterface; + } + + @SuppressWarnings("unchecked") + static FieldBasedShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface) { + return new FieldBasedShapeGenerator<>(storageSuperClass, storageFactoryInterface); + } + + @Override + StaticShape generateShape(StaticShape parentShape, Collection extendedProperties) { + Class generatedStorageClass = generateStorage(storageSuperClass, extendedProperties); + Class generatedFactoryClass = generateFactory(generatedStorageClass, storageFactoryInterface); + for (ExtendedProperty extendedProperty : extendedProperties) { + int offset = getObjectFieldOffset(generatedStorageClass, extendedProperty.getName()); + extendedProperty.getProperty().initOffset(offset); + } + return FieldBasedStaticShape.create(generatedStorageClass, generatedFactoryClass); + } + + private static int getObjectFieldOffset(Class c, String fieldName) { + try { + return Math.toIntExact(UNSAFE.objectFieldOffset(c.getField(fieldName))); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private static String getStorageConstructorDescriptor(Constructor superConstructor) { + return Type.getConstructorDescriptor(superConstructor); + } + + private static void addStorageConstructors(ClassVisitor cv, Class storageSuperClass, String storageSuperName) { + for (Constructor superConstructor : storageSuperClass.getDeclaredConstructors()) { + String storageConstructorDescriptor = getStorageConstructorDescriptor(superConstructor); + String superConstructorDescriptor = storageConstructorDescriptor; + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", storageConstructorDescriptor, null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + int var = 1; + for (Class constructorParameter : superConstructor.getParameterTypes()) { + int loadOpcode = Type.getType(constructorParameter).getOpcode(ILOAD); + mv.visitVarInsn(loadOpcode, var++); + } + mv.visitMethodInsn(INVOKESPECIAL, storageSuperName, "", superConstructorDescriptor, false); + mv.visitInsn(RETURN); + mv.visitMaxs(var + 1, var); + mv.visitEnd(); + } + } + + private static void addFactoryConstructor(ClassVisitor cv) { + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", "(II)V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Class storageFactoryInterface) { + for (Method m : storageFactoryInterface.getMethods()) { + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_FINAL, m.getName(), Type.getMethodDescriptor(m), null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, Type.getInternalName(storageClass)); + mv.visitInsn(DUP); + int var = 1; + StringBuilder constructorDescriptor = new StringBuilder(); + constructorDescriptor.append('('); + for (Class p : m.getParameterTypes()) { + int loadOpcode = Type.getType(p).getOpcode(ILOAD); + mv.visitVarInsn(loadOpcode, var++); + constructorDescriptor.append(Type.getDescriptor(p)); + } + constructorDescriptor.append(")V"); + String storageName = Type.getInternalName(storageClass); + mv.visitMethodInsn(INVOKESPECIAL, storageName, "", constructorDescriptor.toString(), false); + mv.visitInsn(ARETURN); + mv.visitMaxs(var, 1); + mv.visitEnd(); + } + } + + private static Class generateStorage(Class storageSuperClass, Collection extendedProperties) { + String storageSuperName = Type.getInternalName(storageSuperClass); + String storageName = generateStorageName(); + int classWriterFlags = ClassWriter.COMPUTE_MAXS; + ClassWriter storageWriter = new ClassWriter(classWriterFlags); + int storageAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC; + storageWriter.visit(V1_8, storageAccess, storageName, null, storageSuperName, null); + addStorageConstructors(storageWriter, storageSuperClass, storageSuperName); + addStorageFields(storageWriter, extendedProperties); + storageWriter.visitEnd(); + return load(storageName, storageWriter.toByteArray(), storageSuperClass); + } + + @SuppressWarnings("unchecked") + private static Class generateFactory(Class storageClass, Class storageFactoryInterface) { + ClassWriter factoryWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); + int factoryAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC | ACC_FINAL; + String factoryName = generateFactoryName(storageClass); + factoryWriter.visit(V1_8, factoryAccess, factoryName, null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(storageFactoryInterface)}); + addFactoryConstructor(factoryWriter); + addFactoryMethods(factoryWriter, storageClass, storageFactoryInterface); + factoryWriter.visitEnd(); + return (Class) load(factoryName, factoryWriter.toByteArray(), storageClass); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedStaticShape.java new file mode 100644 index 000000000000..9927ba7fec14 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedStaticShape.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +final class FieldBasedStaticShape extends StaticShape { + private FieldBasedStaticShape(Class storageClass) { + super(storageClass); + } + + static FieldBasedStaticShape create(Class generatedStorageClass, Class generatedFactoryClass) { + try { + FieldBasedStaticShape shape = new FieldBasedStaticShape<>(generatedStorageClass); + T factory = generatedFactoryClass.cast(UNSAFE.allocateInstance(generatedFactoryClass)); + shape.setFactory(factory); + return shape; + } catch (InstantiationException e) { + throw new RuntimeException(e); + } + } + + @Override + Object getStorage(Object obj, boolean primitive) { + return cast(obj, storageClass); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 5b231b8b4d1a..3359a85829d4 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -42,6 +42,7 @@ abstract class ShapeGenerator { protected static final Unsafe UNSAFE = getUnsafe(); + private static final boolean FIELD_BASED_STORAGE = Boolean.getBoolean("com.oracle.truffle.espresso.staticobject.FieldBasedStorage"); private static final String DELIMITER = "$$"; private static final AtomicInteger counter = new AtomicInteger(); private static final int JAVA_SPEC_VERSION; @@ -67,11 +68,17 @@ abstract class ShapeGenerator { abstract StaticShape generateShape(StaticShape parentShape, Collection extendedProperties); static ShapeGenerator getShapeGenerator(StaticShape parentShape) { - return getShapeGenerator(parentShape.getStorageClass().getSuperclass(), parentShape.getFactoryInterface()); + Class parentStorageClass = parentShape.getStorageClass(); + Class storageSuperclass = FIELD_BASED_STORAGE ? parentStorageClass : parentStorageClass.getSuperclass(); + return getShapeGenerator(storageSuperclass, parentShape.getFactoryInterface()); } static ShapeGenerator getShapeGenerator(Class storageSuperClass, Class storageFactoryInterface) { - return ArrayBasedShapeGenerator.getShapeGenerator(storageSuperClass, storageFactoryInterface); + if (FIELD_BASED_STORAGE) { + return FieldBasedShapeGenerator.getShapeGenerator(storageSuperClass, storageFactoryInterface); + } else { + return ArrayBasedShapeGenerator.getShapeGenerator(storageSuperClass, storageFactoryInterface); + } } static String generateStorageName() { diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index ee566c503328..d553448ad120 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -22,7 +22,12 @@ */ package com.oracle.truffle.espresso.staticobject; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + public abstract class StaticShape { + protected static final Unsafe UNSAFE = getUnsafe(); protected final Class storageClass; protected T factory; @@ -61,4 +66,18 @@ final Class getFactoryInterface() { assert factory.getClass().getInterfaces().length == 1; return (Class) factory.getClass().getInterfaces()[0]; } + + private static Unsafe getUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException e) { + } + try { + Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafeInstance.setAccessible(true); + return (Unsafe) theUnsafeInstance.get(Unsafe.class); + } catch (Exception e) { + throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); + } + } } From 59aecd21110d70b640f18dd1f4ca8e036c1ad14a Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 16 Apr 2021 17:39:02 +0200 Subject: [PATCH 187/290] Run benchmarks also with field-based storage. --- espresso/ci.jsonnet | 1 + espresso/mx.espresso/mx_espresso_benchmarks.py | 1 + 2 files changed, 2 insertions(+) diff --git a/espresso/ci.jsonnet b/espresso/ci.jsonnet index c4d2355ef2fa..f088313cfb8f 100644 --- a/espresso/ci.jsonnet +++ b/espresso/ci.jsonnet @@ -37,6 +37,7 @@ // AWFY peak perf. benchmarks common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-jdk8-linux-amd64'}, common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', guest_jvm_config='field-based', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-field-based-jdk8-linux-amd64'}, common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-jdk8-linux-amd64'}, common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, diff --git a/espresso/mx.espresso/mx_espresso_benchmarks.py b/espresso/mx.espresso/mx_espresso_benchmarks.py index 43648a2ef1de..912d28e807d5 100644 --- a/espresso/mx.espresso/mx_espresso_benchmarks.py +++ b/espresso/mx.espresso/mx_espresso_benchmarks.py @@ -134,6 +134,7 @@ def run_with_heap(heap, args, timeout, suppressStderr=True, nonZeroIsFatal=False mx_benchmark.java_vm_registry.add_vm(EspressoVm('multi-tier-inline-accessors', ['--experimental-options', '--engine.MultiTier', '--java.InlineFieldAccessors']), _suite) mx_benchmark.java_vm_registry.add_vm(EspressoVm('no-inlining', ['--experimental-options', '--engine.Inlining=false']), _suite) mx_benchmark.java_vm_registry.add_vm(EspressoVm('no-shape-checks', ['--vm.Dcom.oracle.truffle.espresso.staticobject.DisableShapeChecks=true']), _suite) +mx_benchmark.java_vm_registry.add_vm(EspressoVm('field-based', ['--vm.Dcom.oracle.truffle.espresso.staticobject.FieldBasedStorage=true']), _suite) mx_benchmark.java_vm_registry.add_vm(EspressoMinHeapVm(0, 0, 64, 'infinite-overhead', []), _suite) mx_benchmark.java_vm_registry.add_vm(EspressoMinHeapVm(1.5, 0, 2048, '1.5-overhead', []), _suite) From 51c2ef39531577e730ecef6659e79340ad0d991b Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 16 Apr 2021 18:13:40 +0200 Subject: [PATCH 188/290] Rename distribution to avoid redefinition. --- espresso/mx.espresso/suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index 2d7068314b8e..74848d230037 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -288,7 +288,7 @@ }, }, - "ESPRESSO_TESTS": { + "ESPRESSO_STATICOBJECT_TESTS": { "subDir": "src", "dependencies": [ "com.oracle.truffle.espresso.staticobject.test", From 0c757086e3d0e9abafe44aef966c597cffc6bddf Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 16 Apr 2021 18:37:49 +0200 Subject: [PATCH 189/290] Remove unused imports introduced by a rebase. --- .../com/oracle/truffle/espresso/runtime/StaticObject.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index bc2f4ee76dd1..d56b8a318268 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -22,9 +22,6 @@ */ package com.oracle.truffle.espresso.runtime; -import static com.oracle.truffle.api.CompilerDirectives.castExact; -import static com.oracle.truffle.espresso.vm.InterpreterToVM.instanceOf; - import java.lang.reflect.Array; import com.oracle.truffle.api.CompilerAsserts; @@ -40,14 +37,12 @@ import com.oracle.truffle.espresso.impl.ArrayKlass; import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.LinkedKlass; import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.dispatch.BaseInterop; import com.oracle.truffle.espresso.substitutions.Host; -import com.oracle.truffle.espresso.vm.UnsafeAccess; /** * Implementation of the Espresso object model. From be739eb54af30f047a9a0ba7b5272837b37d1f28 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 21 Apr 2021 12:33:27 +0200 Subject: [PATCH 190/290] The Shape holds a compilation-final reference to the storage factory. --- .../com/oracle/truffle/espresso/staticobject/StaticShape.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index d553448ad120..7f2d4095e7d1 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.espresso.staticobject; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import sun.misc.Unsafe; import java.lang.reflect.Field; @@ -29,6 +30,7 @@ public abstract class StaticShape { protected static final Unsafe UNSAFE = getUnsafe(); protected final Class storageClass; + @CompilationFinal // protected T factory; StaticShape(Class storageClass) { From 793aad8f019237b736193403bb2f3eacdd716765 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 21 Apr 2021 12:56:22 +0200 Subject: [PATCH 191/290] Suppress warning relative to double-checked locking. `lockOrForeignMarker` is not volatile, but known implementations of EspressoLock have only volatile and final fields. Their values will therefore be visible to other threads. --- .../src/com/oracle/truffle/espresso/runtime/StaticObject.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index d56b8a318268..5a7fa2c33620 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -43,6 +43,7 @@ import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.dispatch.BaseInterop; import com.oracle.truffle.espresso.substitutions.Host; +import com.oracle.truffle.espresso.substitutions.SuppressFBWarnings; /** * Implementation of the Espresso object model. @@ -259,6 +260,7 @@ public Klass getKlass() { * monitor methods ({@link Object#wait() wait}, {@link Object#notify notify}, and * {@link Object#notifyAll notifyAll}) when used with the built-in monitor lock. */ + @SuppressFBWarnings(value = "DC", justification = "Implementations of EspressoLock have only final and volatile fields") public EspressoLock getLock() { checkNotForeign(); if (isNull(this)) { From 808557825f41c66adc7eb5aec7ffc4f093f85e2f Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 30 Apr 2021 19:18:38 +0200 Subject: [PATCH 192/290] To be consistent with other builders used in Truffle, make `Builder` an inner class of `StaticShape`. --- .../test/BuilderPropertyTest.java | 15 ++- .../staticobject/test/InheritanceTest.java | 7 +- .../staticobject/test/PropertyAccessTest.java | 5 +- .../ArrayBasedShapeGenerator.java | 2 +- .../staticobject/ArrayBasedStaticShape.java | 1 - .../FieldBasedShapeGenerator.java | 2 +- .../espresso/staticobject/ShapeGenerator.java | 2 +- .../espresso/staticobject/StaticShape.java | 97 ++++++++++++++- .../staticobject/StaticShapeBuilder.java | 117 ------------------ .../espresso/impl/LinkedKlassFieldLayout.java | 5 +- 10 files changed, 112 insertions(+), 141 deletions(-) delete mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java index dcb2977bf024..c92ebd372f73 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -27,7 +27,6 @@ import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; @@ -38,7 +37,7 @@ public class BuilderPropertyTest { @Test public void sameBuilderSameProperty() { - StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new StaticProperty(StaticPropertyKind.Int); builder.property(property, "p1", false); try { @@ -52,7 +51,7 @@ public void sameBuilderSameProperty() { @Test public void sameBuilderSameName() throws IllegalArgumentException { - StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty p1 = new StaticProperty(StaticPropertyKind.Int); StaticProperty p2 = new StaticProperty(StaticPropertyKind.Int); builder.property(p1, "p1", false); @@ -67,8 +66,8 @@ public void sameBuilderSameName() throws IllegalArgumentException { @Test public void differentBuildersSameProperty() { - StaticShapeBuilder b1 = StaticShape.newBuilder(); - StaticShapeBuilder b2 = StaticShape.newBuilder(); + StaticShape.Builder b1 = StaticShape.newBuilder(); + StaticShape.Builder b2 = StaticShape.newBuilder(); StaticProperty property = new StaticProperty(StaticPropertyKind.Int); b1.property(property, "p1", false); b2.property(property, "p2", false); @@ -86,7 +85,7 @@ public void differentBuildersSameProperty() { public void propertyName() throws NoSuchFieldException { Assume.assumeFalse(StorageLayout.ARRAY_BASED); - StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new StaticProperty(StaticPropertyKind.Int); String propertyName = "p1"; builder.property(property, propertyName, false); @@ -100,7 +99,7 @@ public void propertyName() throws NoSuchFieldException { public void propertyFinal() throws NoSuchFieldException { Assume.assumeFalse(StorageLayout.ARRAY_BASED); - StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty p1 = new StaticProperty(StaticPropertyKind.Int); StaticProperty p2 = new StaticProperty(StaticPropertyKind.Int); String p1Name = "p1"; @@ -119,7 +118,7 @@ public void propertyFinal() throws NoSuchFieldException { public void propertyKind() throws NoSuchFieldException { Assume.assumeFalse(StorageLayout.ARRAY_BASED); - StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticShape.Builder builder = StaticShape.newBuilder(); for (StaticPropertyKind kind : StaticPropertyKind.values()) { builder.property(new StaticProperty(kind), kind.name(), false); } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java index d59f01e6245f..eaa0e8c321fa 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java @@ -27,7 +27,6 @@ import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; @@ -44,7 +43,7 @@ public interface CustomStaticObjectFactory { @Test public void baseClassInheritance() throws NoSuchFieldException { - StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new StaticProperty(StaticPropertyKind.Int); builder.property(property, "field1", false); StaticShape shape = builder.build(CustomStaticObject.class, CustomStaticObjectFactory.class); @@ -68,7 +67,7 @@ public void baseClassInheritance() throws NoSuchFieldException { @Test public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessException { - StaticShapeBuilder b1 = StaticShape.newBuilder(); + StaticShape.Builder b1 = StaticShape.newBuilder(); StaticProperty s1p1 = new StaticProperty(StaticPropertyKind.Int); StaticProperty s1p2 = new StaticProperty(StaticPropertyKind.Int); b1.property(s1p1, "field1", false); @@ -76,7 +75,7 @@ public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessExc // StaticShape s1 declares 2 properties: s1p1 and s1p2 StaticShape s1 = b1.build(); - StaticShapeBuilder b2 = StaticShape.newBuilder(); + StaticShape.Builder b2 = StaticShape.newBuilder(); StaticProperty s2p1 = new StaticProperty(StaticPropertyKind.Int); b2.property(s2p1, "field1", false); // StaticShape s2: diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index 065249647a33..56330e498dc3 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -26,7 +26,6 @@ import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; @@ -159,7 +158,7 @@ public static void init() { @Theory public void correctAccessors(TestDescriptor descriptor) { - StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new StaticProperty(descriptor.kind); builder.property(property, "property", false); StaticShape shape = builder.build(); @@ -178,7 +177,7 @@ public void correctAccessors(TestDescriptor descriptor) { public void wrongAccessors(TestDescriptor expectedDescriptor, TestDescriptor actualDescriptor) { Assume.assumeFalse(expectedDescriptor.equals(actualDescriptor)); - StaticShapeBuilder builder = StaticShape.newBuilder(); + StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new StaticProperty(expectedDescriptor.kind); builder.property(property, "property", false); StaticShape shape = builder.build(); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 213e901db089..798f8d651a12 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -35,7 +35,7 @@ import com.oracle.truffle.api.impl.asm.Label; import com.oracle.truffle.api.impl.asm.MethodVisitor; import com.oracle.truffle.api.impl.asm.Type; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; +import com.oracle.truffle.espresso.staticobject.StaticShape.ExtendedProperty; import org.graalvm.collections.Pair; import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_FINAL; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java index 4f7145015fe5..babe7d3ddbdd 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -24,7 +24,6 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java index f300fd7e8781..3564f5273ea7 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java @@ -26,7 +26,7 @@ import com.oracle.truffle.api.impl.asm.ClassWriter; import com.oracle.truffle.api.impl.asm.MethodVisitor; import com.oracle.truffle.api.impl.asm.Type; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; +import com.oracle.truffle.espresso.staticobject.StaticShape.ExtendedProperty; import java.lang.reflect.Constructor; import java.lang.reflect.Method; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 3359a85829d4..46b9898ab8b7 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -25,7 +25,7 @@ import com.oracle.truffle.api.impl.asm.ClassVisitor; import com.oracle.truffle.api.impl.asm.FieldVisitor; import com.oracle.truffle.api.impl.asm.Type; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder.ExtendedProperty; +import com.oracle.truffle.espresso.staticobject.StaticShape.ExtendedProperty; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 7f2d4095e7d1..8824c2813500 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -26,6 +26,9 @@ import sun.misc.Unsafe; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Objects; public abstract class StaticShape { protected static final Unsafe UNSAFE = getUnsafe(); @@ -37,8 +40,8 @@ public abstract class StaticShape { this.storageClass = storageClass; } - public static StaticShapeBuilder newBuilder() { - return new StaticShapeBuilder(); + public static Builder newBuilder() { + return new Builder(); } protected final void setFactory(T factory) { @@ -82,4 +85,94 @@ private static Unsafe getUnsafe() { throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); } } + + public static final class Builder { + private final HashMap extendedProperties = new HashMap<>(); + + Builder() { + } + + public Builder property(StaticProperty property, String name, boolean isFinal) { + Objects.requireNonNull(property); + if (extendedProperties.containsKey(name)) { + throw new IllegalArgumentException("This builder already contains a property named '" + name + "'"); + } + for (ExtendedProperty extendedProperty : extendedProperties.values()) { + if (extendedProperty.property.equals(property)) { + throw new IllegalArgumentException("This builder already contains this property"); + } + } + extendedProperties.put(name, new ExtendedProperty(property, name, isFinal)); + return this; + } + + public StaticShape build() { + // The classloader that loaded the default superClass must be able to load the default + // factory. + // Therefore, we can't use java.lang.Object as default superClass. + return build(DefaultStaticObject.class, DefaultStaticObject.DefaultStaticObjectFactory.class); + } + + public StaticShape build(StaticShape parentShape) { + Objects.requireNonNull(parentShape); + ShapeGenerator sg = ShapeGenerator.getShapeGenerator(parentShape); + return build(sg, parentShape); + + } + + public StaticShape build(Class superClass, Class factoryInterface) { + validate(factoryInterface, superClass); + ShapeGenerator sg = ShapeGenerator.getShapeGenerator(superClass, factoryInterface); + return build(sg, null); + } + + private StaticShape build(ShapeGenerator sg, StaticShape parentShape) { + StaticShape shape = sg.generateShape(parentShape, extendedProperties.values()); + for (ExtendedProperty extendedProperty : extendedProperties.values()) { + extendedProperty.property.initShape(shape); + } + return shape; + } + + private static void validate(Class storageFactoryInterface, Class storageSuperClass) { + if (!storageFactoryInterface.isInterface()) { + throw new RuntimeException(storageFactoryInterface.getName() + " must be an interface."); + } + for (Method m : storageFactoryInterface.getMethods()) { + if (!m.getReturnType().isAssignableFrom(storageSuperClass)) { + throw new RuntimeException("The return type of '" + m.getReturnType().getName() + " " + storageFactoryInterface.getName() + "." + m.toString() + "' is not assignable from '" + + storageSuperClass.getName() + "'"); + } + try { + storageSuperClass.getDeclaredConstructor(m.getParameterTypes()); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Method '" + m.toString() + "' does not match any constructor in '" + storageSuperClass.getName() + "'", e); + } + } + } + } + + static final class ExtendedProperty { + private final StaticProperty property; + private final String name; + private final boolean isFinal; + + ExtendedProperty(StaticProperty property, String name, boolean isFinal) { + this.property = property; + this.name = name; + this.isFinal = isFinal; + } + + StaticProperty getProperty() { + return property; + } + + String getName() { + return name; + } + + boolean isFinal() { + return isFinal; + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java deleted file mode 100644 index fd1957bae002..000000000000 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShapeBuilder.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.staticobject; - -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Objects; - -public final class StaticShapeBuilder { - private final HashMap extendedProperties = new HashMap<>(); - - StaticShapeBuilder() { - } - - public StaticShapeBuilder property(StaticProperty property, String name, boolean isFinal) { - Objects.requireNonNull(property); - if (extendedProperties.containsKey(name)) { - throw new IllegalArgumentException("This builder already contains a property named '" + name + "'"); - } - for (ExtendedProperty extendedProperty : extendedProperties.values()) { - if (extendedProperty.property.equals(property)) { - throw new IllegalArgumentException("This builder already contains this property"); - } - } - extendedProperties.put(name, new ExtendedProperty(property, name, isFinal)); - return this; - } - - public StaticShape build() { - // The classloader that loaded the default superClass must be able to load the default - // factory. - // Therefore, we can't use java.lang.Object as default superClass. - return build(DefaultStaticObject.class, DefaultStaticObject.DefaultStaticObjectFactory.class); - } - - public StaticShape build(StaticShape parentShape) { - Objects.requireNonNull(parentShape); - ShapeGenerator sg = ShapeGenerator.getShapeGenerator(parentShape); - return build(sg, parentShape); - - } - - public StaticShape build(Class superClass, Class factoryInterface) { - validate(factoryInterface, superClass); - ShapeGenerator sg = ShapeGenerator.getShapeGenerator(superClass, factoryInterface); - return build(sg, null); - } - - private StaticShape build(ShapeGenerator sg, StaticShape parentShape) { - StaticShape shape = sg.generateShape(parentShape, extendedProperties.values()); - for (ExtendedProperty extendedProperty : extendedProperties.values()) { - extendedProperty.property.initShape(shape); - } - return shape; - } - - private static void validate(Class storageFactoryInterface, Class storageSuperClass) { - if (!storageFactoryInterface.isInterface()) { - throw new RuntimeException(storageFactoryInterface.getName() + " must be an interface."); - } - for (Method m : storageFactoryInterface.getMethods()) { - if (!m.getReturnType().isAssignableFrom(storageSuperClass)) { - throw new RuntimeException("The return type of '" + m.getReturnType().getName() + " " + storageFactoryInterface.getName() + "." + m.toString() + "' is not assignable from '" + - storageSuperClass.getName() + "'"); - } - try { - storageSuperClass.getDeclaredConstructor(m.getParameterTypes()); - } catch (NoSuchMethodException e) { - throw new RuntimeException("Method '" + m.toString() + "' does not match any constructor in '" + storageSuperClass.getName() + "'", e); - } - } - } - - static final class ExtendedProperty { - private final StaticProperty property; - private final String name; - private final boolean isFinal; - - ExtendedProperty(StaticProperty property, String name, boolean isFinal) { - this.property = property; - this.name = name; - this.isFinal = isFinal; - } - - StaticProperty getProperty() { - return property; - } - - String getName() { - return name; - } - - boolean isFinal() { - return isFinal; - } - } -} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java index 3aab7a9dd5eb..85b012698ece 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java @@ -30,7 +30,6 @@ import com.oracle.truffle.espresso.runtime.StaticObject; import com.oracle.truffle.espresso.runtime.StaticObject.StaticObjectFactory; import com.oracle.truffle.espresso.staticobject.StaticShape; -import com.oracle.truffle.espresso.staticobject.StaticShapeBuilder; final class LinkedKlassFieldLayout { final StaticShape instanceShape; @@ -46,8 +45,8 @@ final class LinkedKlassFieldLayout { final int fieldTableLength; LinkedKlassFieldLayout(ParserKlass parserKlass, LinkedKlass superKlass) { - StaticShapeBuilder instanceBuilder = StaticShape.newBuilder(); - StaticShapeBuilder staticBuilder = StaticShape.newBuilder(); + StaticShape.Builder instanceBuilder = StaticShape.newBuilder(); + StaticShape.Builder staticBuilder = StaticShape.newBuilder(); FieldCounter fieldCounter = new FieldCounter(parserKlass); int nextInstanceFieldIndex = 0; From 1989dfaa5de480bc98472bff13f580f47e0ba048 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 30 Apr 2021 19:20:21 +0200 Subject: [PATCH 193/290] Rename `DefaultStaticObjectFactory`. --- .../espresso/staticobject/test/BuilderPropertyTest.java | 7 +++---- .../espresso/staticobject/test/InheritanceTest.java | 5 ++--- .../espresso/staticobject/test/PropertyAccessTest.java | 4 ++-- .../truffle/espresso/staticobject/DefaultStaticObject.java | 2 +- .../oracle/truffle/espresso/staticobject/StaticShape.java | 4 ++-- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java index c92ebd372f73..8af0623e4d9e 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -23,7 +23,6 @@ package com.oracle.truffle.espresso.staticobject.test; import com.oracle.truffle.espresso.staticobject.DefaultStaticObject; -import com.oracle.truffle.espresso.staticobject.DefaultStaticObject.DefaultStaticObjectFactory; import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; @@ -89,7 +88,7 @@ public void propertyName() throws NoSuchFieldException { StaticProperty property = new StaticProperty(StaticPropertyKind.Int); String propertyName = "p1"; builder.property(property, propertyName, false); - StaticShape shape = builder.build(); + StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); Field field = object.getClass().getField(propertyName); Assert.assertEquals(propertyName, field.getName()); @@ -106,7 +105,7 @@ public void propertyFinal() throws NoSuchFieldException { String p2Name = "p2"; builder.property(p1, p1Name, true); builder.property(p2, p2Name, false); - StaticShape shape = builder.build(); + StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); Field f1 = object.getClass().getField(p1Name); Field f2 = object.getClass().getField(p2Name); @@ -122,7 +121,7 @@ public void propertyKind() throws NoSuchFieldException { for (StaticPropertyKind kind : StaticPropertyKind.values()) { builder.property(new StaticProperty(kind), kind.name(), false); } - StaticShape shape = builder.build(); + StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); for (StaticPropertyKind kind : StaticPropertyKind.values()) { Class expectedType; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java index eaa0e8c321fa..ee8f33fa5a42 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java @@ -23,7 +23,6 @@ package com.oracle.truffle.espresso.staticobject.test; import com.oracle.truffle.espresso.staticobject.DefaultStaticObject; -import com.oracle.truffle.espresso.staticobject.DefaultStaticObject.DefaultStaticObjectFactory; import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; @@ -73,7 +72,7 @@ public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessExc b1.property(s1p1, "field1", false); b1.property(s1p2, "field2", false); // StaticShape s1 declares 2 properties: s1p1 and s1p2 - StaticShape s1 = b1.build(); + StaticShape s1 = b1.build(); StaticShape.Builder b2 = StaticShape.newBuilder(); StaticProperty s2p1 = new StaticProperty(StaticPropertyKind.Int); @@ -82,7 +81,7 @@ public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessExc // 1. extends s1 // 2. declares one property: s2p1 // 3. inherits one property from s1: s1p2 - StaticShape s2 = b2.build(s1); + StaticShape s2 = b2.build(s1); DefaultStaticObject object = s2.getFactory().create(); s1p1.setInt(object, 1); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index 56330e498dc3..a9de43a75bbe 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -161,7 +161,7 @@ public void correctAccessors(TestDescriptor descriptor) { StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new StaticProperty(descriptor.kind); builder.property(property, "property", false); - StaticShape shape = builder.build(); + StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); // Check the default value @@ -180,7 +180,7 @@ public void wrongAccessors(TestDescriptor expectedDescriptor, TestDescriptor act StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new StaticProperty(expectedDescriptor.kind); builder.property(property, "property", false); - StaticShape shape = builder.build(); + StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); // Check that wrong getters throw exceptions diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticObject.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticObject.java index f47c3bfa974e..bcf2fd5b8fb2 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticObject.java @@ -23,7 +23,7 @@ package com.oracle.truffle.espresso.staticobject; public class DefaultStaticObject { - public interface DefaultStaticObjectFactory { + public interface Factory { DefaultStaticObject create(); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 8824c2813500..fc78eee18eff 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -106,11 +106,11 @@ public Builder property(StaticProperty property, String name, boolean isFinal) { return this; } - public StaticShape build() { + public StaticShape build() { // The classloader that loaded the default superClass must be able to load the default // factory. // Therefore, we can't use java.lang.Object as default superClass. - return build(DefaultStaticObject.class, DefaultStaticObject.DefaultStaticObjectFactory.class); + return build(DefaultStaticObject.class, DefaultStaticObject.Factory.class); } public StaticShape build(StaticShape parentShape) { From e9bcb1ea0fa129b07a2db62f7c8a431663ea0b85 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 4 May 2021 15:00:52 +0200 Subject: [PATCH 194/290] Ensure that only known implementations can create subclasses of StaticShape. --- .../staticobject/ArrayBasedStaticShape.java | 6 ++++- .../staticobject/FieldBasedStaticShape.java | 7 +++++- .../espresso/staticobject/StaticShape.java | 23 ++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java index babe7d3ddbdd..7d9b67d2b9e7 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -34,6 +34,7 @@ import static com.oracle.truffle.espresso.staticobject.StaticPropertyKind.N_PRIMITIVES; final class ArrayBasedStaticShape extends StaticShape { + private static final PrivilegedToken TOKEN = new ArrayBasedPrivilegedToken(); @CompilationFinal // private static Boolean disableShapeChecks; @CompilationFinal(dimensions = 1) // @@ -42,7 +43,7 @@ final class ArrayBasedStaticShape extends StaticShape { @SuppressWarnings({"unchecked", "rawtypes"}) private ArrayBasedStaticShape(ArrayBasedStaticShape parentShape, Class storageClass, ArrayBasedPropertyLayout propertyLayout) { - super(storageClass); + super(storageClass, TOKEN); if (parentShape == null) { superShapes = new StaticShape[]{this}; } else { @@ -104,6 +105,9 @@ private ArrayBasedPropertyLayout getPropertyLayout() { return propertyLayout; } + private static final class ArrayBasedPrivilegedToken extends PrivilegedToken { + } + /** *

    * Creates a layout for the primitive fields of a given class, and assigns to each field the raw diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedStaticShape.java index 9927ba7fec14..4e2e4b2c2b94 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedStaticShape.java @@ -23,8 +23,10 @@ package com.oracle.truffle.espresso.staticobject; final class FieldBasedStaticShape extends StaticShape { + private static final PrivilegedToken TOKEN = new FieldBasedPrivilegedToken(); + private FieldBasedStaticShape(Class storageClass) { - super(storageClass); + super(storageClass, TOKEN); } static FieldBasedStaticShape create(Class generatedStorageClass, Class generatedFactoryClass) { @@ -42,4 +44,7 @@ static FieldBasedStaticShape create(Class generatedStorageClass, Class Object getStorage(Object obj, boolean primitive) { return cast(obj, storageClass); } + + private static final class FieldBasedPrivilegedToken extends PrivilegedToken { + } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index fc78eee18eff..3f9b84d47af2 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -36,8 +36,11 @@ public abstract class StaticShape { @CompilationFinal // protected T factory; - StaticShape(Class storageClass) { + StaticShape(Class storageClass, PrivilegedToken privilegedToken) { this.storageClass = storageClass; + if (privilegedToken == null) { + throw new AssertionError("Only known implementations can create subclasses of " + StaticShape.class.getName()); + } } public static Builder newBuilder() { @@ -175,4 +178,22 @@ boolean isFinal() { return isFinal; } } + + abstract static class PrivilegedToken { + PrivilegedToken() { + if (!isKnownImplementation()) { + throw new AssertionError("Only known implementations can create a " + PrivilegedToken.class.getName() + ".\nGot: " + getClass().getName()); + } + } + + private boolean isKnownImplementation() { + for (String knownImplementation : new String[]{"com.oracle.truffle.espresso.staticobject.ArrayBasedStaticShape$ArrayBasedPrivilegedToken", + "com.oracle.truffle.espresso.staticobject.FieldBasedStaticShape$FieldBasedPrivilegedToken"}) { + if (getClass().getName().equals(knownImplementation)) { + return true; + } + } + return false; + } + } } From 2bfb2ff7d3830848bcad51be7e52bdab36770d63 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 4 May 2021 15:10:32 +0200 Subject: [PATCH 195/290] Make the constructor of StaticProperty protected and create a public DefaultStaticProperty. --- .../test/BuilderPropertyTest.java | 17 +++++----- .../staticobject/test/InheritanceTest.java | 9 ++--- .../staticobject/test/PropertyAccessTest.java | 5 +-- .../ArrayBasedShapeGenerator.java | 6 ++-- .../staticobject/DefaultStaticProperty.java | 33 +++++++++++++++++++ .../espresso/staticobject/StaticProperty.java | 10 +++--- .../truffle/espresso/impl/ArrayKlass.java | 3 +- .../oracle/truffle/espresso/impl/Klass.java | 3 +- 8 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java index 8af0623e4d9e..30c8abc6bd6d 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -23,6 +23,7 @@ package com.oracle.truffle.espresso.staticobject.test; import com.oracle.truffle.espresso.staticobject.DefaultStaticObject; +import com.oracle.truffle.espresso.staticobject.DefaultStaticProperty; import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; @@ -37,7 +38,7 @@ public class BuilderPropertyTest { @Test public void sameBuilderSameProperty() { StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new StaticProperty(StaticPropertyKind.Int); + StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); builder.property(property, "p1", false); try { // You cannot add the same property twice @@ -51,8 +52,8 @@ public void sameBuilderSameProperty() { @Test public void sameBuilderSameName() throws IllegalArgumentException { StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty p1 = new StaticProperty(StaticPropertyKind.Int); - StaticProperty p2 = new StaticProperty(StaticPropertyKind.Int); + StaticProperty p1 = new DefaultStaticProperty(StaticPropertyKind.Int); + StaticProperty p2 = new DefaultStaticProperty(StaticPropertyKind.Int); builder.property(p1, "p1", false); try { // You cannot add two properties with the same name @@ -67,7 +68,7 @@ public void sameBuilderSameName() throws IllegalArgumentException { public void differentBuildersSameProperty() { StaticShape.Builder b1 = StaticShape.newBuilder(); StaticShape.Builder b2 = StaticShape.newBuilder(); - StaticProperty property = new StaticProperty(StaticPropertyKind.Int); + StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); b1.property(property, "p1", false); b2.property(property, "p2", false); b1.build(); @@ -85,7 +86,7 @@ public void propertyName() throws NoSuchFieldException { Assume.assumeFalse(StorageLayout.ARRAY_BASED); StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new StaticProperty(StaticPropertyKind.Int); + StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); String propertyName = "p1"; builder.property(property, propertyName, false); StaticShape shape = builder.build(); @@ -99,8 +100,8 @@ public void propertyFinal() throws NoSuchFieldException { Assume.assumeFalse(StorageLayout.ARRAY_BASED); StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty p1 = new StaticProperty(StaticPropertyKind.Int); - StaticProperty p2 = new StaticProperty(StaticPropertyKind.Int); + StaticProperty p1 = new DefaultStaticProperty(StaticPropertyKind.Int); + StaticProperty p2 = new DefaultStaticProperty(StaticPropertyKind.Int); String p1Name = "p1"; String p2Name = "p2"; builder.property(p1, p1Name, true); @@ -119,7 +120,7 @@ public void propertyKind() throws NoSuchFieldException { StaticShape.Builder builder = StaticShape.newBuilder(); for (StaticPropertyKind kind : StaticPropertyKind.values()) { - builder.property(new StaticProperty(kind), kind.name(), false); + builder.property(new DefaultStaticProperty(kind), kind.name(), false); } StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java index ee8f33fa5a42..2d0207df825a 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java @@ -23,6 +23,7 @@ package com.oracle.truffle.espresso.staticobject.test; import com.oracle.truffle.espresso.staticobject.DefaultStaticObject; +import com.oracle.truffle.espresso.staticobject.DefaultStaticProperty; import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; @@ -43,7 +44,7 @@ public interface CustomStaticObjectFactory { @Test public void baseClassInheritance() throws NoSuchFieldException { StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new StaticProperty(StaticPropertyKind.Int); + StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); builder.property(property, "field1", false); StaticShape shape = builder.build(CustomStaticObject.class, CustomStaticObjectFactory.class); CustomStaticObject object = shape.getFactory().create(); @@ -67,15 +68,15 @@ public void baseClassInheritance() throws NoSuchFieldException { @Test public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessException { StaticShape.Builder b1 = StaticShape.newBuilder(); - StaticProperty s1p1 = new StaticProperty(StaticPropertyKind.Int); - StaticProperty s1p2 = new StaticProperty(StaticPropertyKind.Int); + StaticProperty s1p1 = new DefaultStaticProperty(StaticPropertyKind.Int); + StaticProperty s1p2 = new DefaultStaticProperty(StaticPropertyKind.Int); b1.property(s1p1, "field1", false); b1.property(s1p2, "field2", false); // StaticShape s1 declares 2 properties: s1p1 and s1p2 StaticShape s1 = b1.build(); StaticShape.Builder b2 = StaticShape.newBuilder(); - StaticProperty s2p1 = new StaticProperty(StaticPropertyKind.Int); + StaticProperty s2p1 = new DefaultStaticProperty(StaticPropertyKind.Int); b2.property(s2p1, "field1", false); // StaticShape s2: // 1. extends s1 diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index a9de43a75bbe..341c7c42e5df 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -23,6 +23,7 @@ package com.oracle.truffle.espresso.staticobject.test; import com.oracle.truffle.espresso.staticobject.DefaultStaticObject; +import com.oracle.truffle.espresso.staticobject.DefaultStaticProperty; import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; @@ -159,7 +160,7 @@ public static void init() { @Theory public void correctAccessors(TestDescriptor descriptor) { StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new StaticProperty(descriptor.kind); + StaticProperty property = new DefaultStaticProperty(descriptor.kind); builder.property(property, "property", false); StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); @@ -178,7 +179,7 @@ public void wrongAccessors(TestDescriptor expectedDescriptor, TestDescriptor act Assume.assumeFalse(expectedDescriptor.equals(actualDescriptor)); StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new StaticProperty(expectedDescriptor.kind); + StaticProperty property = new DefaultStaticProperty(expectedDescriptor.kind); builder.property(property, "property", false); StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 798f8d651a12..2f29c37312ce 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -329,9 +329,9 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl private static Collection generateStorageProperties() { return Arrays.asList( - new ExtendedProperty(new StaticProperty(StaticPropertyKind.BYTE_ARRAY), "primitive", true), - new ExtendedProperty(new StaticProperty(StaticPropertyKind.OBJECT_ARRAY), "object", true), - new ExtendedProperty(new StaticProperty(StaticPropertyKind.Object), "shape", true)); + new ExtendedProperty(new DefaultStaticProperty(StaticPropertyKind.BYTE_ARRAY), "primitive", true), + new ExtendedProperty(new DefaultStaticProperty(StaticPropertyKind.OBJECT_ARRAY), "object", true), + new ExtendedProperty(new DefaultStaticProperty(StaticPropertyKind.Object), "shape", true)); } private static Class generateStorage(Class storageSuperClass) { diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java new file mode 100644 index 000000000000..c655959e065f --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, 2021, 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.staticobject; + +public final class DefaultStaticProperty extends StaticProperty { + DefaultStaticProperty(byte internalKind) { + super(internalKind); + } + + public DefaultStaticProperty(StaticPropertyKind kind) { + super(kind); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 5820d452c2a9..938c95c3c190 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -27,7 +27,7 @@ import java.lang.reflect.Field; import sun.misc.Unsafe; -public class StaticProperty { +public abstract class StaticProperty { private static final Unsafe UNSAFE = getUnsafe(); private final byte internalKind; @CompilationFinal // @@ -40,7 +40,7 @@ public class StaticProperty { this.internalKind = internalKind; } - public StaticProperty(StaticPropertyKind kind) { + protected StaticProperty(StaticPropertyKind kind) { this(getInternalKind(kind)); } @@ -48,21 +48,21 @@ private static byte getInternalKind(StaticPropertyKind kind) { return kind.toByte(); } - void initOffset(int o) { + final void initOffset(int o) { if (this.offset != 0) { throw new RuntimeException("Attempt to reinitialize the offset of a static property. Was it added to more than one builder?"); } this.offset = o; } - void initShape(StaticShape s) { + final void initShape(StaticShape s) { if (this.shape != null) { throw new RuntimeException("Attempt to reinitialize the shape of a static property. Was it added to more than one builder?"); } this.shape = s; } - byte getInternalKind() { + final byte getInternalKind() { return internalKind; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java index 0d9ed24ad9ab..c3aad6ec6d17 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java @@ -43,6 +43,7 @@ import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.runtime.StaticObject; import com.oracle.truffle.espresso.runtime.StaticObject.StaticObjectFactory; +import com.oracle.truffle.espresso.staticobject.DefaultStaticProperty; import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; @@ -54,7 +55,7 @@ public final class ArrayKlass extends Klass { private final Klass elementalType; private final int dimension; - private static final StaticProperty ARRAY_PROPERTY = new StaticProperty(StaticPropertyKind.Object); + private static final StaticProperty ARRAY_PROPERTY = new DefaultStaticProperty(StaticPropertyKind.Object); // This field should be static final, but until we move the static object model we cannot have a // SubstrateVM feature which will allow us to set the right field offsets at image build time. @CompilationFinal // diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index 53de43fb210d..31a59ddd93d5 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -29,6 +29,7 @@ import java.util.Comparator; import java.util.function.IntFunction; +import com.oracle.truffle.espresso.staticobject.DefaultStaticProperty; import com.oracle.truffle.espresso.staticobject.StaticProperty; import com.oracle.truffle.espresso.staticobject.StaticPropertyKind; import com.oracle.truffle.espresso.staticobject.StaticShape; @@ -470,7 +471,7 @@ public int compare(Klass k1, Klass k2) { static final DebugCounter KLASS_LOOKUP_DECLARED_METHOD_COUNT = DebugCounter.create("Klass.lookupDeclaredMethod call count"); static final DebugCounter KLASS_LOOKUP_DECLARED_FIELD_COUNT = DebugCounter.create("Klass.lookupDeclaredField call count"); - private static final StaticProperty FOREIGN_PROPERTY = new StaticProperty(StaticPropertyKind.Object); + private static final StaticProperty FOREIGN_PROPERTY = new DefaultStaticProperty(StaticPropertyKind.Object); // This field should be static final, but until we move the static object model we cannot have a // SubstrateVM feature which will allow us to set the right field offsets at image build time. @CompilationFinal // From 2748ac34dda17c38fc407af5b4f6674d6b616f85 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 4 May 2021 15:46:36 +0200 Subject: [PATCH 196/290] Store extended properties in a LinkedHashMap. --- .../com/oracle/truffle/espresso/staticobject/StaticShape.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 3f9b84d47af2..10a35968d09e 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -28,6 +28,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Objects; public abstract class StaticShape { @@ -90,7 +91,7 @@ private static Unsafe getUnsafe() { } public static final class Builder { - private final HashMap extendedProperties = new HashMap<>(); + private final HashMap extendedProperties = new LinkedHashMap<>(); Builder() { } From 82a2849669eebb0fa9435f8cea88c5ee345d3401 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 4 May 2021 16:20:06 +0200 Subject: [PATCH 197/290] Use Integer.compareUnsigned(). --- .../nodes/helper/EspressoReferenceArrayStoreNode.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java index b603bcf0990a..eb7413043e38 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java @@ -52,14 +52,14 @@ public void arrayStore(StaticObject value, int index, StaticObject array) { } } else { // We must throw ArrayIndexOutOfBoundsException before ArrayStoreException - if (index < 0 || index >= array.length()) { - enterOutOfBound2(); - Meta meta = typeCheck.getMeta(); - throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); - } else { + if (Integer.compareUnsigned(index, array.length()) < 0) { enterArrayStoreEx(); Meta meta = typeCheck.getMeta(); throw meta.throwException(meta.java_lang_ArrayStoreException); + } else { + enterOutOfBound2(); + Meta meta = typeCheck.getMeta(); + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); } } } From 578e717347bb73a3cc05247dbfde92806cef39d4 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 4 May 2021 17:30:22 +0200 Subject: [PATCH 198/290] Revert semplifications to the InternalUtils substitution. --- ...oracle_truffle_espresso_InternalUtils.java | 143 +++++++++--------- 1 file changed, 75 insertions(+), 68 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java index 3f5a80a7848c..c0436c51c1aa 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java @@ -25,87 +25,94 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.espresso.analysis.liveness.LivenessAnalysis; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.LinkedKlass; import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.StaticObject; +import sun.misc.Unsafe; + +import java.util.Arrays; @EspressoSubstitutions public class Target_com_oracle_truffle_espresso_InternalUtils { -// @Substitution -// public static @Host(String[].class) StaticObject -// getUnderlyingPrimitiveFieldArray(@Host(Class.class) StaticObject clazz) { -// int i = 0; -// Klass k = clazz.getMirrorKlass(); -// int maxLen; -// if (k instanceof ObjectKlass) { -// maxLen = ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); -// } else { -// return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), -// StaticObject.EMPTY_ARRAY); -// } -// StaticObject[] result = new StaticObject[maxLen]; -// Meta meta = k.getMeta(); -// StaticObject unused = meta.toGuestString("<>"); -// Arrays.fill(result, unused); -// try { -// while (true) { -// Field f = k.lookupFieldTable(i); -// if (!f.isStatic() && f.getKind().isPrimitive()) { -// for (int j = f.getOffset(); j < f.getOffset() + f.getKind().getByteCount(); j++) { -// result[j] = meta.toGuestString(f.getName()); -// } -// } -// i++; -// } -// } catch (AssertionError | IndexOutOfBoundsException e) { -// -// } -// return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), result); -// } -// -// @Substitution -// public static int getPrimitiveFieldByteCount(@Host(Class.class) StaticObject clazz) { -// Klass k = clazz.getMirrorKlass(); -// if (k instanceof ObjectKlass) { -// return ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); -// } else { -// return 0; -// } -// } + @Substitution + public static @Host(String[].class) StaticObject getUnderlyingPrimitiveFieldArray(@Host(Class.class) StaticObject clazz) { + int i = 0; + Klass k = clazz.getMirrorKlass(); + int maxLen; + if (k instanceof ObjectKlass) { + maxLen = ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); + } else { + return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), + StaticObject.EMPTY_ARRAY); + } + StaticObject[] result = new StaticObject[maxLen]; + Meta meta = k.getMeta(); + StaticObject unused = meta.toGuestString("<>"); + Arrays.fill(result, unused); + try { + while (true) { + Field f = k.lookupFieldTable(i); + if (!f.isStatic() && f.getKind().isPrimitive()) { + for (int j = f.getOffset(); j < f.getOffset() + f.getKind().getByteCount(); j++) { + result[j] = meta.toGuestString(f.getName()); + } + } + i++; + } + } catch (AssertionError | IndexOutOfBoundsException e) { + + } + return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), result); + } + + @Substitution + public static int getPrimitiveFieldByteCount(@Host(Class.class) StaticObject clazz) { + Klass k = clazz.getMirrorKlass(); + if (k instanceof ObjectKlass) { + return ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); + } else { + return 0; + } + } @Substitution public static @Host(String.class) StaticObject toVerboseString(@Host(Object.class) StaticObject self) { return self.getKlass().getMeta().toGuestString(self.toVerboseString()); } -// @Substitution -// public static int bytesUsed(@Host(Class.class) StaticObject clazz) { -// Klass k = clazz.getMirrorKlass(); -// int total = 0; -// if (k.isArray()) { -// // ArrayKlass reference -// total += JavaKind.Int.getByteCount(); -// // null reference for primitive field array -// total += JavaKind.Int.getByteCount(); -// // Header of the Object field array + storing its reference -// total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); -// return total; -// } else { -// LinkedKlass lk = ((ObjectKlass) k).getLinkedKlass(); -// // Bytes used by the primitive fields -// total += lk.getPrimitiveInstanceFieldLastOffset(); -// // Bytes used by the Object fields -// total += lk.getObjectFieldsCount() * JavaKind.Int.getByteCount(); -// // Header of the primitive field array + storing its reference -// total += Unsafe.ARRAY_BYTE_BASE_OFFSET + JavaKind.Int.getByteCount(); -// // Header of the Object field array + storing its reference -// total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); -// // Reference to the Klass object. -// total += JavaKind.Int.getByteCount(); -// return total; -// } -// } + @Substitution + public static int bytesUsed(@Host(Class.class) StaticObject clazz) { + Klass k = clazz.getMirrorKlass(); + int total = 0; + if (k.isArray()) { + // ArrayKlass reference + total += JavaKind.Int.getByteCount(); + // null reference for primitive field array + total += JavaKind.Int.getByteCount(); + // Header of the Object field array + storing its reference + total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); + return total; + } else { + LinkedKlass lk = ((ObjectKlass) k).getLinkedKlass(); + // Bytes used by the primitive fields + total += lk.getPrimitiveInstanceFieldLastOffset(); + // Bytes used by the Object fields + total += lk.getObjectFieldsCount() * JavaKind.Int.getByteCount(); + // Header of the primitive field array + storing its reference + total += Unsafe.ARRAY_BYTE_BASE_OFFSET + JavaKind.Int.getByteCount(); + // Header of the Object field array + storing its reference + total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); + // Reference to the Klass object. + total += JavaKind.Int.getByteCount(); + return total; + } + } @Substitution public static boolean inEspresso() { From 6aaf2a2e8879c2dae3fac35f9ee965ee3bf07842 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 4 May 2021 19:07:57 +0200 Subject: [PATCH 199/290] Support InternalUtils method when using array-based storage. --- ...oracle_truffle_espresso_InternalUtils.java | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java index c0436c51c1aa..f3438e89be8b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java @@ -33,8 +33,10 @@ import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.StaticObject; +import com.oracle.truffle.espresso.staticobject.StaticProperty; import sun.misc.Unsafe; +import java.lang.reflect.InvocationTargetException; import java.util.Arrays; @EspressoSubstitutions @@ -46,10 +48,9 @@ public class Target_com_oracle_truffle_espresso_InternalUtils { Klass k = clazz.getMirrorKlass(); int maxLen; if (k instanceof ObjectKlass) { - maxLen = ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); + maxLen = getPrimitiveInstanceFieldLastOffset(((ObjectKlass) k).getLinkedKlass()); } else { - return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), - StaticObject.EMPTY_ARRAY); + return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), StaticObject.EMPTY_ARRAY); } StaticObject[] result = new StaticObject[maxLen]; Meta meta = k.getMeta(); @@ -59,7 +60,7 @@ public class Target_com_oracle_truffle_espresso_InternalUtils { while (true) { Field f = k.lookupFieldTable(i); if (!f.isStatic() && f.getKind().isPrimitive()) { - for (int j = f.getOffset(); j < f.getOffset() + f.getKind().getByteCount(); j++) { + for (int j = getOffset(f); j < getOffset(f) + f.getKind().getByteCount(); j++) { result[j] = meta.toGuestString(f.getName()); } } @@ -75,7 +76,7 @@ public class Target_com_oracle_truffle_espresso_InternalUtils { public static int getPrimitiveFieldByteCount(@Host(Class.class) StaticObject clazz) { Klass k = clazz.getMirrorKlass(); if (k instanceof ObjectKlass) { - return ((ObjectKlass) k).getLinkedKlass().getPrimitiveInstanceFieldLastOffset(); + return getPrimitiveInstanceFieldLastOffset(((ObjectKlass) k).getLinkedKlass()); } else { return 0; } @@ -101,9 +102,9 @@ public static int bytesUsed(@Host(Class.class) StaticObject clazz) { } else { LinkedKlass lk = ((ObjectKlass) k).getLinkedKlass(); // Bytes used by the primitive fields - total += lk.getPrimitiveInstanceFieldLastOffset(); + total += getPrimitiveInstanceFieldLastOffset(lk); // Bytes used by the Object fields - total += lk.getObjectFieldsCount() * JavaKind.Int.getByteCount(); + total += getObjectFieldsCount(lk) * JavaKind.Int.getByteCount(); // Header of the primitive field array + storing its reference total += Unsafe.ARRAY_BYTE_BASE_OFFSET + JavaKind.Int.getByteCount(); // Header of the Object field array + storing its reference @@ -125,4 +126,38 @@ public static void triggerLivenessAnalysis(@Host(java.lang.reflect.Method.class) Method m = Method.getHostReflectiveMethodRoot(method, meta); LivenessAnalysis.analyze(m); } + + private static int getOffset(Field f) { + try { + java.lang.reflect.Field linkedFieldField = Field.class.getDeclaredField("linkedField"); + linkedFieldField.setAccessible(true); + Object linkedField = linkedFieldField.get(f); + java.lang.reflect.Field offsetField = StaticProperty.class.getDeclaredField("offset"); + offsetField.setAccessible(true); + return offsetField.getInt(linkedField); + } catch(IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private static int getPrimitiveInstanceFieldLastOffset(LinkedKlass lk) { + return getLayoutProperty(lk, "lastOffset"); + } + + private static int getObjectFieldsCount(LinkedKlass lk) { + return getLayoutProperty(lk, "objectArraySize"); + } + + private static int getLayoutProperty(LinkedKlass lk, String propertyName) { + try { + java.lang.reflect.Method getPropertyLayout = Class.forName("com.oracle.truffle.espresso.staticobject.ArrayBasedStaticShape").getDeclaredMethod("getPropertyLayout"); + getPropertyLayout.setAccessible(true); + Object propertyLayout = getPropertyLayout.invoke(lk.getShape(false)); + java.lang.reflect.Field lastOffset = propertyLayout.getClass().getDeclaredField(propertyName); + lastOffset.setAccessible(true); + return lastOffset.getInt(propertyLayout); + } catch(ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchFieldException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } } From 16888dcae34c7c0ce9c97542e05450fed7cbf750 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 4 May 2021 19:43:05 +0200 Subject: [PATCH 200/290] Fix check for array-based storage used for unit tests. --- .../truffle/espresso/staticobject/test/StorageLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java index e44ff2837670..fcebfb2a0989 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java @@ -23,5 +23,5 @@ package com.oracle.truffle.espresso.staticobject.test; class StorageLayout { - static final boolean ARRAY_BASED = true; + static final boolean ARRAY_BASED = !Boolean.getBoolean("com.oracle.truffle.espresso.staticobject.FieldBasedStorage"); } From 0f6e7763845b98a5e850971cd75b177e75429139 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 5 May 2021 11:35:30 +0200 Subject: [PATCH 201/290] Remove recursive call from `EspressoInterop.asInstant()`. The cached `interopLibrary` expects a constant `receiver` class, which is not the case when using the field-based storage since `Instant`, `ZonedDateTime`, and `Date` are represented by different generated classes. --- .../runtime/dispatch/EspressoInterop.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/EspressoInterop.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/EspressoInterop.java index 9eb8a0e12a83..41ef47e17e1d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/EspressoInterop.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/EspressoInterop.java @@ -1168,24 +1168,21 @@ static boolean isTimeZone(StaticObject receiver) { @CachedLibrary("receiver") InteropLibrary receiverLibrary, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException { receiver.checkNotForeign(); if (receiverLibrary.isInstant(receiver)) { + StaticObject instant; Meta meta = receiver.getKlass().getMeta(); - if (instanceOf(receiver, meta.java_time_Instant)) { - long seconds = (long) meta.java_time_Instant_seconds.get(receiver); - int nanos = (int) meta.java_time_Instant_nanos.get(receiver); - return Instant.ofEpochSecond(seconds, nanos); - } else if (instanceOf(receiver, meta.java_time_ZonedDateTime)) { - StaticObject instant = (StaticObject) meta.java_time_ZonedDateTime_toInstant.invokeDirect(receiver); - // Interop library should be compatible. - assert receiverLibrary.accepts(instant); - return asInstant(instant, receiverLibrary, error); + if (instanceOf(receiver, meta.java_time_ZonedDateTime)) { + instant = (StaticObject) meta.java_time_ZonedDateTime_toInstant.invokeDirect(receiver); } else if (instanceOf(receiver, meta.java_util_Date)) { int index = meta.java_util_Date_toInstant.getVTableIndex(); Method virtualToInstant = receiver.getKlass().vtableLookup(index); - StaticObject instant = (StaticObject) virtualToInstant.invokeDirect(receiver); - // Interop library should be compatible. - assert receiverLibrary.accepts(instant); - return asInstant(instant, receiverLibrary, error); + instant = (StaticObject) virtualToInstant.invokeDirect(receiver); + } else { + instant = receiver; } + assert instanceOf(instant, meta.java_time_Instant); + long seconds = (long) meta.java_time_Instant_seconds.get(instant); + int nanos = (int) meta.java_time_Instant_nanos.get(instant); + return Instant.ofEpochSecond(seconds, nanos); } error.enter(); throw UnsupportedMessageException.create(); From 5221af2afb6a6a70406ace7d45de884fc483878e Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 5 May 2021 11:49:59 +0200 Subject: [PATCH 202/290] Remove outdated class. --- ...oracle_truffle_espresso_InternalUtils.java | 163 ------------------ 1 file changed, 163 deletions(-) delete mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java deleted file mode 100644 index f3438e89be8b..000000000000 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_InternalUtils.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2019, 2019, 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.substitutions; - -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.espresso.analysis.liveness.LivenessAnalysis; -import com.oracle.truffle.espresso.impl.Field; -import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.LinkedKlass; -import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.impl.ObjectKlass; -import com.oracle.truffle.espresso.meta.JavaKind; -import com.oracle.truffle.espresso.meta.Meta; -import com.oracle.truffle.espresso.runtime.StaticObject; -import com.oracle.truffle.espresso.staticobject.StaticProperty; -import sun.misc.Unsafe; - -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; - -@EspressoSubstitutions -public class Target_com_oracle_truffle_espresso_InternalUtils { - - @Substitution - public static @Host(String[].class) StaticObject getUnderlyingPrimitiveFieldArray(@Host(Class.class) StaticObject clazz) { - int i = 0; - Klass k = clazz.getMirrorKlass(); - int maxLen; - if (k instanceof ObjectKlass) { - maxLen = getPrimitiveInstanceFieldLastOffset(((ObjectKlass) k).getLinkedKlass()); - } else { - return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), StaticObject.EMPTY_ARRAY); - } - StaticObject[] result = new StaticObject[maxLen]; - Meta meta = k.getMeta(); - StaticObject unused = meta.toGuestString("<>"); - Arrays.fill(result, unused); - try { - while (true) { - Field f = k.lookupFieldTable(i); - if (!f.isStatic() && f.getKind().isPrimitive()) { - for (int j = getOffset(f); j < getOffset(f) + f.getKind().getByteCount(); j++) { - result[j] = meta.toGuestString(f.getName()); - } - } - i++; - } - } catch (AssertionError | IndexOutOfBoundsException e) { - - } - return StaticObject.createArray(k.getMeta().java_lang_String.getArrayClass(), result); - } - - @Substitution - public static int getPrimitiveFieldByteCount(@Host(Class.class) StaticObject clazz) { - Klass k = clazz.getMirrorKlass(); - if (k instanceof ObjectKlass) { - return getPrimitiveInstanceFieldLastOffset(((ObjectKlass) k).getLinkedKlass()); - } else { - return 0; - } - } - - @Substitution - public static @Host(String.class) StaticObject toVerboseString(@Host(Object.class) StaticObject self) { - return self.getKlass().getMeta().toGuestString(self.toVerboseString()); - } - - @Substitution - public static int bytesUsed(@Host(Class.class) StaticObject clazz) { - Klass k = clazz.getMirrorKlass(); - int total = 0; - if (k.isArray()) { - // ArrayKlass reference - total += JavaKind.Int.getByteCount(); - // null reference for primitive field array - total += JavaKind.Int.getByteCount(); - // Header of the Object field array + storing its reference - total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); - return total; - } else { - LinkedKlass lk = ((ObjectKlass) k).getLinkedKlass(); - // Bytes used by the primitive fields - total += getPrimitiveInstanceFieldLastOffset(lk); - // Bytes used by the Object fields - total += getObjectFieldsCount(lk) * JavaKind.Int.getByteCount(); - // Header of the primitive field array + storing its reference - total += Unsafe.ARRAY_BYTE_BASE_OFFSET + JavaKind.Int.getByteCount(); - // Header of the Object field array + storing its reference - total += Unsafe.ARRAY_OBJECT_BASE_OFFSET + JavaKind.Int.getByteCount(); - // Reference to the Klass object. - total += JavaKind.Int.getByteCount(); - return total; - } - } - - @Substitution - public static boolean inEspresso() { - return true; - } - - @CompilerDirectives.TruffleBoundary - @Substitution - public static void triggerLivenessAnalysis(@Host(java.lang.reflect.Method.class) StaticObject method, @InjectMeta Meta meta) { - Method m = Method.getHostReflectiveMethodRoot(method, meta); - LivenessAnalysis.analyze(m); - } - - private static int getOffset(Field f) { - try { - java.lang.reflect.Field linkedFieldField = Field.class.getDeclaredField("linkedField"); - linkedFieldField.setAccessible(true); - Object linkedField = linkedFieldField.get(f); - java.lang.reflect.Field offsetField = StaticProperty.class.getDeclaredField("offset"); - offsetField.setAccessible(true); - return offsetField.getInt(linkedField); - } catch(IllegalAccessException | NoSuchFieldException e) { - throw new RuntimeException(e); - } - } - - private static int getPrimitiveInstanceFieldLastOffset(LinkedKlass lk) { - return getLayoutProperty(lk, "lastOffset"); - } - - private static int getObjectFieldsCount(LinkedKlass lk) { - return getLayoutProperty(lk, "objectArraySize"); - } - - private static int getLayoutProperty(LinkedKlass lk, String propertyName) { - try { - java.lang.reflect.Method getPropertyLayout = Class.forName("com.oracle.truffle.espresso.staticobject.ArrayBasedStaticShape").getDeclaredMethod("getPropertyLayout"); - getPropertyLayout.setAccessible(true); - Object propertyLayout = getPropertyLayout.invoke(lk.getShape(false)); - java.lang.reflect.Field lastOffset = propertyLayout.getClass().getDeclaredField(propertyName); - lastOffset.setAccessible(true); - return lastOffset.getInt(propertyLayout); - } catch(ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchFieldException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } -} From 17b0f4bb2bf06dc6e94ef372c69e92ff222dc416 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 5 May 2021 18:45:33 +0200 Subject: [PATCH 203/290] Add assertions that methods in `StaticShape.Builder` are not reachable during Truffle compilation. --- .../com/oracle/truffle/espresso/staticobject/StaticShape.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 10a35968d09e..074ba8f3ccc5 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.espresso.staticobject; +import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import sun.misc.Unsafe; @@ -97,6 +98,7 @@ public static final class Builder { } public Builder property(StaticProperty property, String name, boolean isFinal) { + CompilerAsserts.neverPartOfCompilation(); Objects.requireNonNull(property); if (extendedProperties.containsKey(name)) { throw new IllegalArgumentException("This builder already contains a property named '" + name + "'"); @@ -131,6 +133,7 @@ public StaticShape build(Class superClass, Class factoryInterface) } private StaticShape build(ShapeGenerator sg, StaticShape parentShape) { + CompilerAsserts.neverPartOfCompilation(); StaticShape shape = sg.generateShape(parentShape, extendedProperties.values()); for (ExtendedProperty extendedProperty : extendedProperties.values()) { extendedProperty.property.initShape(shape); @@ -139,6 +142,7 @@ private StaticShape build(ShapeGenerator sg, StaticShape parentShap } private static void validate(Class storageFactoryInterface, Class storageSuperClass) { + CompilerAsserts.neverPartOfCompilation(); if (!storageFactoryInterface.isInterface()) { throw new RuntimeException(storageFactoryInterface.getName() + " must be an interface."); } From f2bb3aa113b958f60030ca67e1baa742aa05876f Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 12:47:52 +0200 Subject: [PATCH 204/290] Simplify EspressoReferenceArrayStoreNode. --- .../EspressoReferenceArrayStoreNode.java | 43 ++++++------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java index eb7413043e38..9a9abbe6b633 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java @@ -33,8 +33,7 @@ public class EspressoReferenceArrayStoreNode extends Node { @Child TypeCheckNode typeCheck; - @CompilationFinal boolean noOutOfBoundEx1 = true; - @CompilationFinal boolean noOutOfBoundEx2 = true; + @CompilationFinal boolean noOutOfBoundEx = true; @CompilationFinal boolean noArrayStoreEx = true; public EspressoReferenceArrayStoreNode(EspressoContext context) { @@ -42,39 +41,23 @@ public EspressoReferenceArrayStoreNode(EspressoContext context) { } public void arrayStore(StaticObject value, int index, StaticObject array) { - if (StaticObject.isNull(value) || typeCheck.executeTypeCheck(((ArrayKlass) array.getKlass()).getComponentType(), value.getKlass())) { - try { - (array. unwrap())[index] = value; - } catch (ArrayIndexOutOfBoundsException e) { - enterOutOfBound1(); - Meta meta = typeCheck.getMeta(); - throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); - } - } else { - // We must throw ArrayIndexOutOfBoundsException before ArrayStoreException - if (Integer.compareUnsigned(index, array.length()) < 0) { - enterArrayStoreEx(); - Meta meta = typeCheck.getMeta(); - throw meta.throwException(meta.java_lang_ArrayStoreException); - } else { - enterOutOfBound2(); - Meta meta = typeCheck.getMeta(); - throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); - } + if (Integer.compareUnsigned(index, array.length()) >= 0) { + enterOutOfBound(); + Meta meta = typeCheck.getMeta(); + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); } - } - - private void enterOutOfBound1() { - if (noOutOfBoundEx1) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noOutOfBoundEx1 = false; + if (!StaticObject.isNull(value) && !typeCheck.executeTypeCheck(((ArrayKlass) array.getKlass()).getComponentType(), value.getKlass())) { + enterArrayStoreEx(); + Meta meta = typeCheck.getMeta(); + throw meta.throwException(meta.java_lang_ArrayStoreException); } + (array. unwrap())[index] = value; } - private void enterOutOfBound2() { - if (noOutOfBoundEx2) { + private void enterOutOfBound() { + if (noOutOfBoundEx) { CompilerDirectives.transferToInterpreterAndInvalidate(); - noOutOfBoundEx2 = false; + noOutOfBoundEx = false; } } From cc62f02cd8caed91b0cdb80bd2cdde066cf06fb0 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 12:56:58 +0200 Subject: [PATCH 205/290] Simplify assertion. --- .../src/com/oracle/truffle/espresso/runtime/StaticObject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 5a7fa2c33620..46a21eea26ff 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -106,7 +106,7 @@ private void initStaticFields(ObjectKlass thisKlass) { } public static StaticObject createNew(ObjectKlass klass) { - assert klass != null && !klass.isAbstract() && !klass.isInterface(); + assert !klass.isAbstract() && !klass.isInterface(); StaticObject newObj = klass.getLinkedKlass().getShape(false).getFactory().create(klass); newObj.initInstanceFields(klass); return trackAllocation(klass, newObj); From 8b788738aed48798675f744b8e39e450411870df Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 13:11:06 +0200 Subject: [PATCH 206/290] Remove unnecessary truffle boundary. --- .../src/com/oracle/truffle/espresso/runtime/StaticObject.java | 1 - 1 file changed, 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 46a21eea26ff..d22fea913382 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -193,7 +193,6 @@ public StaticObject copy() { } @Override - @TruffleBoundary public Object clone() { try { return super.clone(); From 0ed74ad875fc15096f55560b22569877571103e3 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 16:45:08 +0200 Subject: [PATCH 207/290] Give more meaningful names to local variables. --- .../ArrayBasedShapeGenerator.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 2f29c37312ce..4b6a665fa682 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -178,29 +178,29 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, // primitive = primitiveArraySize > 0 ? new byte[primitiveArraySize] : null; mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ILOAD, var + 1); - Label label2 = new Label(); - mv.visitJumpInsn(IFLE, label2); + Label lNoPrimitives = new Label(); + mv.visitJumpInsn(IFLE, lNoPrimitives); mv.visitVarInsn(ILOAD, var + 1); mv.visitIntInsn(NEWARRAY, T_BYTE); - Label label3 = new Label(); - mv.visitJumpInsn(GOTO, label3); - mv.visitLabel(label2); + Label lSetPrimitive = new Label(); + mv.visitJumpInsn(GOTO, lSetPrimitive); + mv.visitLabel(lNoPrimitives); mv.visitInsn(ACONST_NULL); - mv.visitLabel(label3); + mv.visitLabel(lSetPrimitive); mv.visitFieldInsn(PUTFIELD, storageName, "primitive", "[B"); // object = objectArraySize > 0 ? new Object[objectArraySize] : null; mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ILOAD, var + 2); - Label label5 = new Label(); - mv.visitJumpInsn(IFLE, label5); + Label lNoObjects = new Label(); + mv.visitJumpInsn(IFLE, lNoObjects); mv.visitVarInsn(ILOAD, var + 2); mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); - Label label6 = new Label(); - mv.visitJumpInsn(GOTO, label6); - mv.visitLabel(label5); + Label lSetObject = new Label(); + mv.visitJumpInsn(GOTO, lSetObject); + mv.visitLabel(lNoObjects); mv.visitInsn(ACONST_NULL); - mv.visitLabel(label6); + mv.visitLabel(lSetObject); mv.visitFieldInsn(PUTFIELD, storageName, "object", "[Ljava/lang/Object;"); mv.visitInsn(RETURN); @@ -229,36 +229,36 @@ private static void addCloneMethod(ClassVisitor cv, String className) { mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, "primitive", "[B"); - Label label2 = new Label(); - mv.visitJumpInsn(IFNONNULL, label2); + Label lHasPrimitives = new Label(); + mv.visitJumpInsn(IFNONNULL, lHasPrimitives); mv.visitInsn(ACONST_NULL); - Label label3 = new Label(); - mv.visitJumpInsn(GOTO, label3); - mv.visitLabel(label2); + Label lSetPrimitive = new Label(); + mv.visitJumpInsn(GOTO, lSetPrimitive); + mv.visitLabel(lHasPrimitives); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, "primitive", "[B"); mv.visitMethodInsn(INVOKEVIRTUAL, "[B", "clone", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, "[B"); mv.visitTypeInsn(CHECKCAST, "[B"); - mv.visitLabel(label3); + mv.visitLabel(lSetPrimitive); mv.visitFieldInsn(PUTFIELD, className, "primitive", "[B"); // clone.object = (object == null ? null : (Object[]) object.clone()); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, "object", "[Ljava/lang/Object;"); - Label label5 = new Label(); - mv.visitJumpInsn(IFNONNULL, label5); + Label lHasObjects = new Label(); + mv.visitJumpInsn(IFNONNULL, lHasObjects); mv.visitInsn(ACONST_NULL); - Label label6 = new Label(); - mv.visitJumpInsn(GOTO, label6); - mv.visitLabel(label5); + Label lSetObject = new Label(); + mv.visitJumpInsn(GOTO, lSetObject); + mv.visitLabel(lHasObjects); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, "object", "[Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "[Ljava/lang/Object;", "clone", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;"); mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;"); - mv.visitLabel(label6); + mv.visitLabel(lSetObject); mv.visitFieldInsn(PUTFIELD, className, "object", "[Ljava/lang/Object;"); mv.visitVarInsn(ALOAD, 1); From 7f18ab560e41f748e767f7822bc5446ff2fcd7dd Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 17:07:36 +0200 Subject: [PATCH 208/290] Simplify code and explain why we must call `this.clone()`. --- .../truffle/espresso/runtime/StaticObject.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index d22fea913382..ea97bce44362 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -187,20 +187,17 @@ public StaticObject copy() { if (getKlass().isArray()) { obj = createArray((ArrayKlass) getKlass(), cloneWrappedArray()); } else { - obj = (StaticObject) clone(); + try { + // Call `this.clone()` rather than `super.clone()` to execute the `clone()` methods + // of generated subtypes. + obj = (StaticObject) clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } } return trackAllocation(getKlass(), obj); } - @Override - public Object clone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - private static StaticObject trackAllocation(Klass klass, StaticObject obj) { if (klass == null) { return obj; From 97e0cd757a5561fd437e46ac887e0e078fbe2c72 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 18:17:16 +0200 Subject: [PATCH 209/290] Call `super.clone()` rather than `Object.clone()`. --- .../ArrayBasedShapeGenerator.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 4b6a665fa682..2ba3b14ce42e 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -210,12 +210,27 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, } } - private static void addCloneMethod(ClassVisitor cv, String className) { - // TODO(da): should the descriptor and the exceptions match those of `super.clone()`? - MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "clone", "()Ljava/lang/Object;", null, new String[]{"java/lang/CloneNotSupportedException"}); + private static Method getCloneMethod(Class storageSuperClass) { + for (Class clazz = storageSuperClass; clazz != null; clazz = clazz.getSuperclass()) { + try { + return clazz.getDeclaredMethod("clone"); + } catch (NoSuchMethodException e) { + // Swallow the error, check the super class + } + } + throw new RuntimeException("Should not reach here"); + } + + private static String[] getCloneMethodExceptions(Method cloneMethod) { + return Arrays.stream(cloneMethod.getExceptionTypes()).map(c -> Type.getInternalName(c)).toArray(String[]::new); + } + + private static void addCloneMethod(Class storageSuperClass, ClassVisitor cv, String className) { + Method superCloneMethod = getCloneMethod(storageSuperClass); + String superCloneMethodDescriptor = Type.getMethodDescriptor(superCloneMethod); + MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "clone", superCloneMethodDescriptor, null, getCloneMethodExceptions(superCloneMethod)); mv.visitVarInsn(ALOAD, 0); - // TODO(da): we need to call `super.clone()`, not `java.lang.Object.clone()` - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "clone", "()Ljava/lang/Object;", false); + mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(storageSuperClass), "clone", superCloneMethodDescriptor, false); mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, 1); @@ -346,7 +361,7 @@ private static Class generateStorage(Class storageSuperClass) { addStorageConstructors(storageWriter, storageName, storageSuperClass, storageSuperName); addStorageFields(storageWriter, arrayProperties); if (Cloneable.class.isAssignableFrom(storageSuperClass)) { - addCloneMethod(storageWriter, storageName); + addCloneMethod(storageSuperClass, storageWriter, storageName); } storageWriter.visitEnd(); return load(storageName, storageWriter.toByteArray(), storageSuperClass); From d2144212ceeb45db40831c725bcc87d7bd69dc35 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 18:25:54 +0200 Subject: [PATCH 210/290] The `shape` field is already set by `clone()`. --- .../espresso/staticobject/ArrayBasedShapeGenerator.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 2ba3b14ce42e..ac0a41a9527a 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -234,12 +234,6 @@ private static void addCloneMethod(Class storageSuperClass, ClassVisitor cv, mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, 1); - // clone.shape = shape; - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, className, "shape", "Ljava/lang/Object;"); - mv.visitFieldInsn(PUTFIELD, className, "shape", "Ljava/lang/Object;"); - // clone.primitive = (primitive == null ? null : (byte[]) primitive.clone()); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); From 4d2b3293ae37630e2ec49521b133f738c475e93e Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 18:26:10 +0200 Subject: [PATCH 211/290] Do not allocate String arrays all the times. --- .../espresso/staticobject/ArrayBasedShapeGenerator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index ac0a41a9527a..8c40c7317896 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -66,6 +66,7 @@ final class ArrayBasedShapeGenerator extends ShapeGenerator { private static final int UNINITIALIZED_NATIVE_OFFSET = -1; private static final ConcurrentHashMap, Class>, ArrayBasedShapeGenerator> generatorCache = new ConcurrentHashMap<>(); + private static final String[] ARRAY_SIZE_FIELDS = new String[]{"primitiveArraySize", "objectArraySize"}; private final Class generatedStorageClass; private final Class generatedFactoryClass; @@ -320,7 +321,7 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, factoryName, "shape", "Ljava/lang/Object;"); var++; - for (String fieldName : new String[]{"primitiveArraySize", "objectArraySize"}) { + for (String fieldName : ARRAY_SIZE_FIELDS) { constructorDescriptor.append("I"); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, factoryName, fieldName, "I"); From 493a14c70ec84d933f1dc960939578ed33436d72 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 19:08:25 +0200 Subject: [PATCH 212/290] Fix computation of max stack and max locals. --- .../ArrayBasedShapeGenerator.java | 24 ++++++++++--------- .../FieldBasedShapeGenerator.java | 21 ++++++++-------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 8c40c7317896..10b4df037ef1 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -308,31 +308,34 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl mv.visitCode(); mv.visitTypeInsn(NEW, Type.getInternalName(storageClass)); mv.visitInsn(DUP); - int var = 1; + int maxStack = 2; StringBuilder constructorDescriptor = new StringBuilder(); constructorDescriptor.append('('); - for (Class p : m.getParameterTypes()) { - int loadOpcode = Type.getType(p).getOpcode(ILOAD); - mv.visitVarInsn(loadOpcode, var++); - constructorDescriptor.append(Type.getDescriptor(p)); + Class[] params = m.getParameterTypes(); + for (int i = 0; i < params.length; i++) { + int loadOpcode = Type.getType(params[i]).getOpcode(ILOAD); + mv.visitVarInsn(loadOpcode, i + 1); + constructorDescriptor.append(Type.getDescriptor(params[i])); + maxStack++; } + int maxLocals = maxStack - 1; constructorDescriptor.append("Ljava/lang/Object;"); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, factoryName, "shape", "Ljava/lang/Object;"); - var++; + maxStack++; for (String fieldName : ARRAY_SIZE_FIELDS) { constructorDescriptor.append("I"); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, factoryName, fieldName, "I"); - var++; + maxStack++; } constructorDescriptor.append(")V"); String storageName = Type.getInternalName(storageClass); mv.visitMethodInsn(INVOKESPECIAL, storageName, "", constructorDescriptor.toString(), false); mv.visitInsn(ARETURN); - mv.visitMaxs(var, 1); + mv.visitMaxs(maxStack, maxLocals); mv.visitEnd(); } } @@ -349,8 +352,7 @@ private static Class generateStorage(Class storageSuperClass) { String storageName = generateStorageName(); Collection arrayProperties = generateStorageProperties(); - int classWriterFlags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS; - ClassWriter storageWriter = new ClassWriter(classWriterFlags); + ClassWriter storageWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); int storageAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC; storageWriter.visit(V1_8, storageAccess, storageName, null, storageSuperName, null); addStorageConstructors(storageWriter, storageName, storageSuperClass, storageSuperName); @@ -364,7 +366,7 @@ private static Class generateStorage(Class storageSuperClass) { @SuppressWarnings("unchecked") private static Class generateFactory(Class storageClass, Class storageFactoryInterface) { - ClassWriter factoryWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); + ClassWriter factoryWriter = new ClassWriter(0); int factoryAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC | ACC_FINAL; String factoryName = generateFactoryName(storageClass); factoryWriter.visit(V1_8, factoryAccess, factoryName, null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(storageFactoryInterface)}); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java index 3564f5273ea7..afbfa33f0358 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java @@ -107,7 +107,7 @@ private static void addFactoryConstructor(ClassVisitor cv) { mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false); mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); + mv.visitMaxs(1, 3); mv.visitEnd(); } @@ -117,19 +117,21 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl mv.visitCode(); mv.visitTypeInsn(NEW, Type.getInternalName(storageClass)); mv.visitInsn(DUP); - int var = 1; + int maxStack = 2; StringBuilder constructorDescriptor = new StringBuilder(); constructorDescriptor.append('('); - for (Class p : m.getParameterTypes()) { - int loadOpcode = Type.getType(p).getOpcode(ILOAD); - mv.visitVarInsn(loadOpcode, var++); - constructorDescriptor.append(Type.getDescriptor(p)); + Class[] params = m.getParameterTypes(); + for (int i = 0; i < params.length; i++) { + int loadOpcode = Type.getType(params[i]).getOpcode(ILOAD); + mv.visitVarInsn(loadOpcode, i + 1); + constructorDescriptor.append(Type.getDescriptor(params[i])); + maxStack++; } constructorDescriptor.append(")V"); String storageName = Type.getInternalName(storageClass); mv.visitMethodInsn(INVOKESPECIAL, storageName, "", constructorDescriptor.toString(), false); mv.visitInsn(ARETURN); - mv.visitMaxs(var, 1); + mv.visitMaxs(maxStack, maxStack - 1); mv.visitEnd(); } } @@ -137,8 +139,7 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl private static Class generateStorage(Class storageSuperClass, Collection extendedProperties) { String storageSuperName = Type.getInternalName(storageSuperClass); String storageName = generateStorageName(); - int classWriterFlags = ClassWriter.COMPUTE_MAXS; - ClassWriter storageWriter = new ClassWriter(classWriterFlags); + ClassWriter storageWriter = new ClassWriter(0); int storageAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC; storageWriter.visit(V1_8, storageAccess, storageName, null, storageSuperName, null); addStorageConstructors(storageWriter, storageSuperClass, storageSuperName); @@ -149,7 +150,7 @@ private static Class generateStorage(Class storageSuperClass, Collection Class generateFactory(Class storageClass, Class storageFactoryInterface) { - ClassWriter factoryWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); + ClassWriter factoryWriter = new ClassWriter(0); int factoryAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC | ACC_FINAL; String factoryName = generateFactoryName(storageClass); factoryWriter.visit(V1_8, factoryAccess, factoryName, null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(storageFactoryInterface)}); From 64b49d6c643c8649d39ec1cdc3fc8dd91047209e Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 19:21:48 +0200 Subject: [PATCH 213/290] Explain why `disableShapeChecks` is not a static final field. --- .../truffle/espresso/staticobject/ArrayBasedStaticShape.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java index 7d9b67d2b9e7..666924f829e8 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -65,6 +65,9 @@ public static boolean shapeChecks() { @CompilerDirectives.TruffleBoundary private static synchronized void initializeShapeChecks() { if (disableShapeChecks == null) { + // Eventually this will become a context option. + // For now we store its value in a static field that is initialized on first usage to + // avoid that it gets initialized at native-image build time. disableShapeChecks = Boolean.getBoolean("com.oracle.truffle.espresso.staticobject.DisableShapeChecks"); } } From 70948eb703ee05247ee7d08fc8f0a851d439fcd1 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 19:43:12 +0200 Subject: [PATCH 214/290] Simplify check for properties inserted multiple times. --- .../truffle/espresso/staticobject/StaticShape.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 074ba8f3ccc5..fec1982e6751 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -29,6 +29,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Objects; @@ -93,6 +94,7 @@ private static Unsafe getUnsafe() { public static final class Builder { private final HashMap extendedProperties = new LinkedHashMap<>(); + private final HashSet staticProperties = new HashSet<>(); Builder() { } @@ -103,12 +105,11 @@ public Builder property(StaticProperty property, String name, boolean isFinal) { if (extendedProperties.containsKey(name)) { throw new IllegalArgumentException("This builder already contains a property named '" + name + "'"); } - for (ExtendedProperty extendedProperty : extendedProperties.values()) { - if (extendedProperty.property.equals(property)) { - throw new IllegalArgumentException("This builder already contains this property"); - } + if (staticProperties.contains(property)) { + throw new IllegalArgumentException("This builder already contains this property"); } extendedProperties.put(name, new ExtendedProperty(property, name, isFinal)); + staticProperties.add(property); return this; } From cf389c0d9eacd9c6b04f3e25606c54030c74915b Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 19:43:48 +0200 Subject: [PATCH 215/290] Remove empty and unreachable line. --- .../com/oracle/truffle/espresso/staticobject/StaticShape.java | 1 - 1 file changed, 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index fec1982e6751..4b3978dfd957 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -124,7 +124,6 @@ public StaticShape build(StaticShape parentShape) { Objects.requireNonNull(parentShape); ShapeGenerator sg = ShapeGenerator.getShapeGenerator(parentShape); return build(sg, parentShape); - } public StaticShape build(Class superClass, Class factoryInterface) { From 713c3857282c163102c680dd9cfd18598e748d58 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 19:45:07 +0200 Subject: [PATCH 216/290] Fix comment. --- .../truffle/espresso/staticobject/test/InheritanceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java index 2d0207df825a..e0e25a63f558 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java @@ -91,7 +91,7 @@ public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessExc s1p2.setInt(object, 2); Assert.assertEquals(2, s1p2.getInt(object)); s2p1.setInt(object, 3); - // s1p1 accesses the field declared in + // s1p1 accesses the field declared in s1 Assert.assertEquals(1, s1p1.getInt(object)); Assert.assertEquals(3, s2p1.getInt(object)); From acefe26114f83d5e33d43e9ef0636de376c84d32 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 20:05:31 +0200 Subject: [PATCH 217/290] Use more extreme test value. --- .../truffle/espresso/staticobject/test/PropertyAccessTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index 341c7c42e5df..0401187d6d30 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -98,7 +98,7 @@ public static void init() { case Char: descriptors[i] = new TestDescriptor( StaticPropertyKind.Char, - (char) 42, + (char) 0x0102, (char) 0, (p, obj) -> p.getChar(obj), (p, obj, val) -> p.setChar(obj, (char) val)); From 99079c5086f99a2f89d4ca33a176bc9aa5970478 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 20:06:25 +0200 Subject: [PATCH 218/290] Test what happens when assigning a value to an object of the wrong shape. --- .../staticobject/test/PropertyAccessTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index 0401187d6d30..849d684b0b32 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -200,6 +200,30 @@ public void wrongAccessors(TestDescriptor expectedDescriptor, TestDescriptor act } } + @Theory + public void wrongShape(TestDescriptor descriptor) { + StaticShape.Builder b1 = StaticShape.newBuilder(); + StaticProperty p1 = new DefaultStaticProperty(descriptor.kind); + b1.property(p1, "property", false); + StaticShape s1 = b1.build(); + DefaultStaticObject o1 = s1.getFactory().create(); + + StaticShape.Builder b2 = StaticShape.newBuilder(); + StaticProperty p2 = new DefaultStaticProperty(descriptor.kind); + b2.property(p2, "property", false); + StaticShape s2 = b2.build(); + DefaultStaticObject o2 = s2.getFactory().create(); + + try { + descriptor.setter.set(p1, o2, descriptor.testValue); + Assert.fail(); + } catch (ClassCastException e) { + Assert.assertTrue(!StorageLayout.ARRAY_BASED); + } catch (RuntimeException e) { + Assert.assertTrue(e.getMessage().startsWith("Incompatible shape on property access.")); + } + } + @Test public void dummy() { // to make sure this file is recognized as a test From c340dc8b96fcd16cb5b19caf12eaa4057a474a59 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 20:08:54 +0200 Subject: [PATCH 219/290] Cloning a StaticObject should never throw an exception. --- .../src/com/oracle/truffle/espresso/runtime/StaticObject.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index ea97bce44362..9ddbc57eef64 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -192,7 +192,8 @@ public StaticObject copy() { // of generated subtypes. obj = (StaticObject) clone(); } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); + EspressoError.shouldNotReachHere(e); + obj = null; } } return trackAllocation(getKlass(), obj); From 57c311e1c161ff1fda061bb7fa6cda33adbd1db7 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 20:28:15 +0200 Subject: [PATCH 220/290] Suppress warning for unused variable. We need to build the StaticShape s1 so that the StaticProperty p1 is initialized. --- .../truffle/espresso/staticobject/test/PropertyAccessTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index 849d684b0b32..1c6a5e6b2917 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -201,12 +201,12 @@ public void wrongAccessors(TestDescriptor expectedDescriptor, TestDescriptor act } @Theory + @SuppressWarnings("unused") public void wrongShape(TestDescriptor descriptor) { StaticShape.Builder b1 = StaticShape.newBuilder(); StaticProperty p1 = new DefaultStaticProperty(descriptor.kind); b1.property(p1, "property", false); StaticShape s1 = b1.build(); - DefaultStaticObject o1 = s1.getFactory().create(); StaticShape.Builder b2 = StaticShape.newBuilder(); StaticProperty p2 = new DefaultStaticProperty(descriptor.kind); From a9054d0dee38d118c57c1fc7fc68857254fe765e Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Fri, 7 May 2021 22:30:31 +0200 Subject: [PATCH 221/290] Add a truffle boundary on `clone()`. It is blacklisted by Native Image. --- .../com/oracle/truffle/espresso/runtime/StaticObject.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 9ddbc57eef64..d60f7700ce46 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -199,6 +199,12 @@ public StaticObject copy() { return trackAllocation(getKlass(), obj); } + @Override + @TruffleBoundary + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + private static StaticObject trackAllocation(Klass klass, StaticObject obj) { if (klass == null) { return obj; From a59a0677b16b733b9cd8ac9b55a1fc95611d61a9 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 10 May 2021 14:59:48 +0200 Subject: [PATCH 222/290] Use more extreme test values. --- .../staticobject/test/PropertyAccessTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index 1c6a5e6b2917..f28edfc9548c 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -90,7 +90,7 @@ public static void init() { case Byte: descriptors[i] = new TestDescriptor( StaticPropertyKind.Byte, - (byte) 42, + (byte) 0x01, (byte) 0, (p, obj) -> p.getByte(obj), (p, obj, val) -> p.setByte(obj, (byte) val)); @@ -98,7 +98,7 @@ public static void init() { case Char: descriptors[i] = new TestDescriptor( StaticPropertyKind.Char, - (char) 0x0102, + (char) 0x0203, (char) 0, (p, obj) -> p.getChar(obj), (p, obj, val) -> p.setChar(obj, (char) val)); @@ -106,7 +106,7 @@ public static void init() { case Double: descriptors[i] = new TestDescriptor( StaticPropertyKind.Double, - 42D, + Double.longBitsToDouble(0x161718191a1b1c1dL), 0D, (p, obj) -> p.getDouble(obj), (p, obj, val) -> p.setDouble(obj, (double) val)); @@ -114,7 +114,7 @@ public static void init() { case Float: descriptors[i] = new TestDescriptor( StaticPropertyKind.Float, - 42F, + Float.intBitsToFloat(0x12131415), 0F, (p, obj) -> p.getFloat(obj), (p, obj, val) -> p.setFloat(obj, (float) val)); @@ -122,7 +122,7 @@ public static void init() { case Int: descriptors[i] = new TestDescriptor( StaticPropertyKind.Int, - 42, + 0x0607_0809, 0, (p, obj) -> p.getInt(obj), (p, obj, val) -> p.setInt(obj, (int) val)); @@ -130,7 +130,7 @@ public static void init() { case Long: descriptors[i] = new TestDescriptor( StaticPropertyKind.Long, - 42L, + 0x0a0b_0c0d_0e0f_10_11L, 0L, (p, obj) -> p.getLong(obj), (p, obj, val) -> p.setLong(obj, (long) val)); @@ -138,7 +138,7 @@ public static void init() { case Short: descriptors[i] = new TestDescriptor( StaticPropertyKind.Short, - (short) 42, + (short) 0x0405, (short) 0, (p, obj) -> p.getShort(obj), (p, obj, val) -> p.setShort(obj, (short) val)); From 4df8d5011bde4376c9976c22b490a2b6c3027eff Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 10 May 2021 15:07:58 +0200 Subject: [PATCH 223/290] Remove unnecessary check for properties registered multiple times. It duplicated a similar check on property initialization. --- .../staticobject/test/BuilderPropertyTest.java | 11 ++++++----- .../truffle/espresso/staticobject/StaticProperty.java | 4 ++-- .../truffle/espresso/staticobject/StaticShape.java | 5 ----- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java index 30c8abc6bd6d..55df4171f5fb 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -40,12 +40,13 @@ public void sameBuilderSameProperty() { StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); builder.property(property, "p1", false); + builder.property(property, "p2", false); try { - // You cannot add the same property twice - builder.property(property, "p2", false); + builder.build(); Assert.fail(); - } catch (IllegalArgumentException e) { - Assert.assertEquals("This builder already contains this property", e.getMessage()); + } catch (RuntimeException e) { + // You cannot add the same property twice + Assert.assertEquals("Attempt to reinitialize the offset of a static property. Was it added to more than one builder or multiple times to the same builder?", e.getMessage()); } } @@ -77,7 +78,7 @@ public void differentBuildersSameProperty() { b2.build(); Assert.fail(); } catch (RuntimeException e) { - Assert.assertEquals("Attempt to reinitialize the offset of a static property. Was it added to more than one builder?", e.getMessage()); + Assert.assertEquals("Attempt to reinitialize the offset of a static property. Was it added to more than one builder or multiple times to the same builder?", e.getMessage()); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 938c95c3c190..c1419a7d90c9 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -50,14 +50,14 @@ private static byte getInternalKind(StaticPropertyKind kind) { final void initOffset(int o) { if (this.offset != 0) { - throw new RuntimeException("Attempt to reinitialize the offset of a static property. Was it added to more than one builder?"); + throw new RuntimeException("Attempt to reinitialize the offset of a static property. Was it added to more than one builder or multiple times to the same builder?"); } this.offset = o; } final void initShape(StaticShape s) { if (this.shape != null) { - throw new RuntimeException("Attempt to reinitialize the shape of a static property. Was it added to more than one builder?"); + throw new RuntimeException("Attempt to reinitialize the shape of a static property. Was it added to more than one builder or multiple times to the same builder?"); } this.shape = s; } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 4b3978dfd957..7df98f4da96b 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -94,7 +94,6 @@ private static Unsafe getUnsafe() { public static final class Builder { private final HashMap extendedProperties = new LinkedHashMap<>(); - private final HashSet staticProperties = new HashSet<>(); Builder() { } @@ -105,11 +104,7 @@ public Builder property(StaticProperty property, String name, boolean isFinal) { if (extendedProperties.containsKey(name)) { throw new IllegalArgumentException("This builder already contains a property named '" + name + "'"); } - if (staticProperties.contains(property)) { - throw new IllegalArgumentException("This builder already contains this property"); - } extendedProperties.put(name, new ExtendedProperty(property, name, isFinal)); - staticProperties.add(property); return this; } From c132381e5e164649ca889210933be4774bb43314 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 10 May 2021 15:08:59 +0200 Subject: [PATCH 224/290] Turn check into an assertion. `StaticShape` can only be allocated by `ArrayBasedStaticShape` and `FieldBasedStaticShape`, and in both cases this happens just before the call to `setFactory()`. --- .../com/oracle/truffle/espresso/staticobject/StaticShape.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 7df98f4da96b..6f7d81b22d7d 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -51,9 +51,7 @@ public static Builder newBuilder() { } protected final void setFactory(T factory) { - if (this.factory != null) { - throw new RuntimeException("Attempt to reinitialize the offset of a static property. Was it added to more than one builder?"); - } + assert this.factory == null; this.factory = factory; } From 0926ea8a3a07a01d1b27504a36384fa1cab21315 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 10 May 2021 15:11:10 +0200 Subject: [PATCH 225/290] Avoid unreachable assignment. --- .../src/com/oracle/truffle/espresso/runtime/StaticObject.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index d60f7700ce46..d4ad5827728d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -192,8 +192,7 @@ public StaticObject copy() { // of generated subtypes. obj = (StaticObject) clone(); } catch (CloneNotSupportedException e) { - EspressoError.shouldNotReachHere(e); - obj = null; + throw EspressoError.shouldNotReachHere(e); } } return trackAllocation(getKlass(), obj); From d030af81e2940c10bfbf1964336a8c2de074d765 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 10 May 2021 17:58:08 +0200 Subject: [PATCH 226/290] Compute frames of generated methods with jumps. --- .../ArrayBasedShapeGenerator.java | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 10b4df037ef1..69dd714552ae 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -34,6 +34,7 @@ import com.oracle.truffle.api.impl.asm.ClassWriter; import com.oracle.truffle.api.impl.asm.Label; import com.oracle.truffle.api.impl.asm.MethodVisitor; +import com.oracle.truffle.api.impl.asm.Opcodes; import com.oracle.truffle.api.impl.asm.Type; import com.oracle.truffle.espresso.staticobject.StaticShape.ExtendedProperty; import org.graalvm.collections.Pair; @@ -48,14 +49,19 @@ import static com.oracle.truffle.api.impl.asm.Opcodes.ARETURN; import static com.oracle.truffle.api.impl.asm.Opcodes.ASTORE; import static com.oracle.truffle.api.impl.asm.Opcodes.CHECKCAST; +import static com.oracle.truffle.api.impl.asm.Opcodes.DOUBLE; import static com.oracle.truffle.api.impl.asm.Opcodes.DUP; +import static com.oracle.truffle.api.impl.asm.Opcodes.FLOAT; +import static com.oracle.truffle.api.impl.asm.Opcodes.F_FULL; import static com.oracle.truffle.api.impl.asm.Opcodes.GETFIELD; import static com.oracle.truffle.api.impl.asm.Opcodes.GOTO; import static com.oracle.truffle.api.impl.asm.Opcodes.IFLE; import static com.oracle.truffle.api.impl.asm.Opcodes.IFNONNULL; import static com.oracle.truffle.api.impl.asm.Opcodes.ILOAD; +import static com.oracle.truffle.api.impl.asm.Opcodes.INTEGER; import static com.oracle.truffle.api.impl.asm.Opcodes.INVOKESPECIAL; import static com.oracle.truffle.api.impl.asm.Opcodes.INVOKEVIRTUAL; +import static com.oracle.truffle.api.impl.asm.Opcodes.LONG; import static com.oracle.truffle.api.impl.asm.Opcodes.NEW; import static com.oracle.truffle.api.impl.asm.Opcodes.NEWARRAY; import static com.oracle.truffle.api.impl.asm.Opcodes.PUTFIELD; @@ -156,6 +162,37 @@ private static String getStorageConstructorDescriptor(Constructor superConstr return sb.append(")V").toString(); } + private static Object getFrameLocal(Class clazz) { + if (clazz.isPrimitive()) { + if (clazz == Boolean.TYPE || clazz == Byte.TYPE || clazz == Character.TYPE || clazz == Integer.TYPE || clazz == Short.TYPE) { + return INTEGER; + } else if (clazz == Double.TYPE) { + return DOUBLE; + } else if (clazz == Float.TYPE) { + return FLOAT; + } else if (clazz == Long.TYPE) { + return LONG; + } else { + throw new AssertionError(); + } + } else { + return Type.getInternalName(clazz); + } + } + + private static Object[] getFrameLocals(String storageName, Class[] constructorParameters) { + Object[] frameLocals = new Object[constructorParameters.length + 4]; + int idx = 0; + frameLocals[idx++] = storageName; // this + for (; idx <= constructorParameters.length; idx++) { + frameLocals[idx] = getFrameLocal(constructorParameters[idx - 1]); + } + frameLocals[idx++] = "java/lang/Object"; // shape + frameLocals[idx++] = INTEGER; // primitiveArraySize + frameLocals[idx++] = INTEGER; // objectArraySize + return frameLocals; + } + private static void addStorageConstructors(ClassVisitor cv, String storageName, Class storageSuperClass, String storageSuperName) { for (Constructor superConstructor : storageSuperClass.getDeclaredConstructors()) { String storageConstructorDescriptor = getStorageConstructorDescriptor(superConstructor); @@ -165,12 +202,18 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, mv.visitCode(); mv.visitVarInsn(ALOAD, 0); int var = 1; - for (Class constructorParameter : superConstructor.getParameterTypes()) { - int loadOpcode = Type.getType(constructorParameter).getOpcode(ILOAD); + Class[] constructorParameters = superConstructor.getParameterTypes(); + for (int i = 0; i < constructorParameters.length; i++) { + Class constructorParameter = constructorParameters[i]; + Type parameterType = Type.getType(constructorParameter); + int loadOpcode = parameterType.getOpcode(ILOAD); mv.visitVarInsn(loadOpcode, var++); } mv.visitMethodInsn(INVOKESPECIAL, storageSuperName, "", superConstructorDescriptor, false); + // Prepare array of frame locals for jumps + Object[] frameLocals = getFrameLocals(storageName, constructorParameters); + // this.shape = shape; mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, var); @@ -186,8 +229,10 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, Label lSetPrimitive = new Label(); mv.visitJumpInsn(GOTO, lSetPrimitive); mv.visitLabel(lNoPrimitives); + mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 1, new Object[] {storageName}); mv.visitInsn(ACONST_NULL); mv.visitLabel(lSetPrimitive); + mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 2, new Object[] {storageName, "[B"}); mv.visitFieldInsn(PUTFIELD, storageName, "primitive", "[B"); // object = objectArraySize > 0 ? new Object[objectArraySize] : null; @@ -200,8 +245,10 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, Label lSetObject = new Label(); mv.visitJumpInsn(GOTO, lSetObject); mv.visitLabel(lNoObjects); + mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 1, new Object[] {storageName}); mv.visitInsn(ACONST_NULL); mv.visitLabel(lSetObject); + mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 2, new Object[] {storageName, "[Ljava/lang/Object;"}); mv.visitFieldInsn(PUTFIELD, storageName, "object", "[Ljava/lang/Object;"); mv.visitInsn(RETURN); @@ -227,6 +274,9 @@ private static String[] getCloneMethodExceptions(Method cloneMethod) { } private static void addCloneMethod(Class storageSuperClass, ClassVisitor cv, String className) { + // Prepare array of frame locals for jumps + Object[] frameLocals = new Object[] {className, className}; + Method superCloneMethod = getCloneMethod(storageSuperClass); String superCloneMethodDescriptor = Type.getMethodDescriptor(superCloneMethod); MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "clone", superCloneMethodDescriptor, null, getCloneMethodExceptions(superCloneMethod)); @@ -245,12 +295,14 @@ private static void addCloneMethod(Class storageSuperClass, ClassVisitor cv, Label lSetPrimitive = new Label(); mv.visitJumpInsn(GOTO, lSetPrimitive); mv.visitLabel(lHasPrimitives); + mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 1, new Object[] {className}); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, "primitive", "[B"); mv.visitMethodInsn(INVOKEVIRTUAL, "[B", "clone", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, "[B"); mv.visitTypeInsn(CHECKCAST, "[B"); mv.visitLabel(lSetPrimitive); + mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 2, new Object[] {className, "[B"}); mv.visitFieldInsn(PUTFIELD, className, "primitive", "[B"); // clone.object = (object == null ? null : (Object[]) object.clone()); @@ -263,12 +315,14 @@ private static void addCloneMethod(Class storageSuperClass, ClassVisitor cv, Label lSetObject = new Label(); mv.visitJumpInsn(GOTO, lSetObject); mv.visitLabel(lHasObjects); + mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 1, new Object[] {className}); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, "object", "[Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "[Ljava/lang/Object;", "clone", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;"); mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;"); mv.visitLabel(lSetObject); + mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 2, new Object[] {className, "[Ljava/lang/Object;"}); mv.visitFieldInsn(PUTFIELD, className, "object", "[Ljava/lang/Object;"); mv.visitVarInsn(ALOAD, 1); @@ -352,7 +406,7 @@ private static Class generateStorage(Class storageSuperClass) { String storageName = generateStorageName(); Collection arrayProperties = generateStorageProperties(); - ClassWriter storageWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + ClassWriter storageWriter = new ClassWriter(0); int storageAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC; storageWriter.visit(V1_8, storageAccess, storageName, null, storageSuperName, null); addStorageConstructors(storageWriter, storageName, storageSuperClass, storageSuperName); From e91ca094d7e172e363b59ecd760e8d5bb741ee8f Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Mon, 10 May 2021 19:42:16 +0200 Subject: [PATCH 227/290] Static properties must return their name and if they are final. --- .../test/BuilderPropertyTest.java | 47 ++++++++++--------- .../staticobject/test/InheritanceTest.java | 16 +++---- .../staticobject/test/PropertyAccessTest.java | 18 +++---- .../ArrayBasedShapeGenerator.java | 33 +++++++------ .../staticobject/ArrayBasedStaticShape.java | 16 +++---- .../staticobject/DefaultStaticProperty.java | 21 +++++++-- .../FieldBasedShapeGenerator.java | 15 +++--- .../espresso/staticobject/ShapeGenerator.java | 11 ++--- .../espresso/staticobject/StaticProperty.java | 12 +++-- .../espresso/staticobject/StaticShape.java | 41 ++++------------ .../truffle/espresso/impl/ArrayKlass.java | 4 +- .../oracle/truffle/espresso/impl/Field.java | 2 +- .../oracle/truffle/espresso/impl/Klass.java | 4 +- .../truffle/espresso/impl/LinkedField.java | 12 ++++- .../espresso/impl/LinkedKlassFieldLayout.java | 6 +-- .../truffle/espresso/impl/ObjectKlass.java | 2 +- 16 files changed, 132 insertions(+), 128 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java index 55df4171f5fb..8b0a26c9a1cc 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -38,30 +38,30 @@ public class BuilderPropertyTest { @Test public void sameBuilderSameProperty() { StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); - builder.property(property, "p1", false); - builder.property(property, "p2", false); + StaticProperty property = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); + builder.property(property); try { - builder.build(); + // You cannot add the same property twice + builder.property(property); Assert.fail(); - } catch (RuntimeException e) { + } catch (IllegalArgumentException e) { // You cannot add the same property twice - Assert.assertEquals("Attempt to reinitialize the offset of a static property. Was it added to more than one builder or multiple times to the same builder?", e.getMessage()); + Assert.assertEquals("This builder already contains a property named 'property'", e.getMessage()); } } @Test public void sameBuilderSameName() throws IllegalArgumentException { StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty p1 = new DefaultStaticProperty(StaticPropertyKind.Int); - StaticProperty p2 = new DefaultStaticProperty(StaticPropertyKind.Int); - builder.property(p1, "p1", false); + StaticProperty p1 = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); + StaticProperty p2 = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); + builder.property(p1); try { // You cannot add two properties with the same name - builder.property(p2, "p1", false); + builder.property(p2); Assert.fail(); } catch (IllegalArgumentException e) { - Assert.assertEquals("This builder already contains a property named 'p1'", e.getMessage()); + Assert.assertEquals("This builder already contains a property named 'property'", e.getMessage()); } } @@ -69,16 +69,17 @@ public void sameBuilderSameName() throws IllegalArgumentException { public void differentBuildersSameProperty() { StaticShape.Builder b1 = StaticShape.newBuilder(); StaticShape.Builder b2 = StaticShape.newBuilder(); - StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); - b1.property(property, "p1", false); - b2.property(property, "p2", false); + StaticProperty property = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); + b1.property(property); + b2.property(property); b1.build(); try { // You cannot build shapes that share properties b2.build(); Assert.fail(); } catch (RuntimeException e) { - Assert.assertEquals("Attempt to reinitialize the offset of a static property. Was it added to more than one builder or multiple times to the same builder?", e.getMessage()); + Assert.assertEquals("Attempt to reinitialize the offset of static property 'property' of kind 'Int'.\nWas it added to more than one builder or multiple times to the same builder?", + e.getMessage()); } } @@ -87,9 +88,9 @@ public void propertyName() throws NoSuchFieldException { Assume.assumeFalse(StorageLayout.ARRAY_BASED); StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); - String propertyName = "p1"; - builder.property(property, propertyName, false); + String propertyName = "property"; + StaticProperty property = new DefaultStaticProperty(propertyName, StaticPropertyKind.Int, false); + builder.property(property); StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); Field field = object.getClass().getField(propertyName); @@ -101,12 +102,12 @@ public void propertyFinal() throws NoSuchFieldException { Assume.assumeFalse(StorageLayout.ARRAY_BASED); StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty p1 = new DefaultStaticProperty(StaticPropertyKind.Int); - StaticProperty p2 = new DefaultStaticProperty(StaticPropertyKind.Int); String p1Name = "p1"; String p2Name = "p2"; - builder.property(p1, p1Name, true); - builder.property(p2, p2Name, false); + StaticProperty p1 = new DefaultStaticProperty(p1Name, StaticPropertyKind.Int, true); + StaticProperty p2 = new DefaultStaticProperty(p2Name, StaticPropertyKind.Int, false); + builder.property(p1); + builder.property(p2); StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); Field f1 = object.getClass().getField(p1Name); @@ -121,7 +122,7 @@ public void propertyKind() throws NoSuchFieldException { StaticShape.Builder builder = StaticShape.newBuilder(); for (StaticPropertyKind kind : StaticPropertyKind.values()) { - builder.property(new DefaultStaticProperty(kind), kind.name(), false); + builder.property(new DefaultStaticProperty(kind.name(), kind, false)); } StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java index e0e25a63f558..df7f4014da83 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java @@ -44,8 +44,8 @@ public interface CustomStaticObjectFactory { @Test public void baseClassInheritance() throws NoSuchFieldException { StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new DefaultStaticProperty(StaticPropertyKind.Int); - builder.property(property, "field1", false); + StaticProperty property = new DefaultStaticProperty("field1", StaticPropertyKind.Int, false); + builder.property(property); StaticShape shape = builder.build(CustomStaticObject.class, CustomStaticObjectFactory.class); CustomStaticObject object = shape.getFactory().create(); @@ -68,16 +68,16 @@ public void baseClassInheritance() throws NoSuchFieldException { @Test public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessException { StaticShape.Builder b1 = StaticShape.newBuilder(); - StaticProperty s1p1 = new DefaultStaticProperty(StaticPropertyKind.Int); - StaticProperty s1p2 = new DefaultStaticProperty(StaticPropertyKind.Int); - b1.property(s1p1, "field1", false); - b1.property(s1p2, "field2", false); + StaticProperty s1p1 = new DefaultStaticProperty("field1", StaticPropertyKind.Int, false); + StaticProperty s1p2 = new DefaultStaticProperty("field2", StaticPropertyKind.Int, false); + b1.property(s1p1); + b1.property(s1p2); // StaticShape s1 declares 2 properties: s1p1 and s1p2 StaticShape s1 = b1.build(); StaticShape.Builder b2 = StaticShape.newBuilder(); - StaticProperty s2p1 = new DefaultStaticProperty(StaticPropertyKind.Int); - b2.property(s2p1, "field1", false); + StaticProperty s2p1 = new DefaultStaticProperty("field1", StaticPropertyKind.Int, false); + b2.property(s2p1); // StaticShape s2: // 1. extends s1 // 2. declares one property: s2p1 diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index f28edfc9548c..ade9ca960c5d 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -160,8 +160,8 @@ public static void init() { @Theory public void correctAccessors(TestDescriptor descriptor) { StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new DefaultStaticProperty(descriptor.kind); - builder.property(property, "property", false); + StaticProperty property = new DefaultStaticProperty("property", descriptor.kind, false); + builder.property(property); StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); @@ -179,13 +179,13 @@ public void wrongAccessors(TestDescriptor expectedDescriptor, TestDescriptor act Assume.assumeFalse(expectedDescriptor.equals(actualDescriptor)); StaticShape.Builder builder = StaticShape.newBuilder(); - StaticProperty property = new DefaultStaticProperty(expectedDescriptor.kind); - builder.property(property, "property", false); + StaticProperty property = new DefaultStaticProperty("property", expectedDescriptor.kind, false); + builder.property(property); StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); // Check that wrong getters throw exceptions - String expectedExceptionMessage = "Static property of '" + expectedDescriptor.kind.name() + "' kind cannot be accessed as '" + actualDescriptor.kind + "'"; + String expectedExceptionMessage = "Static property 'property' of kind '" + expectedDescriptor.kind.name() + "' cannot be accessed as '" + actualDescriptor.kind + "'"; try { actualDescriptor.getter.get(property, object); Assert.fail(); @@ -204,13 +204,13 @@ public void wrongAccessors(TestDescriptor expectedDescriptor, TestDescriptor act @SuppressWarnings("unused") public void wrongShape(TestDescriptor descriptor) { StaticShape.Builder b1 = StaticShape.newBuilder(); - StaticProperty p1 = new DefaultStaticProperty(descriptor.kind); - b1.property(p1, "property", false); + StaticProperty p1 = new DefaultStaticProperty("property", descriptor.kind, false); + b1.property(p1); StaticShape s1 = b1.build(); StaticShape.Builder b2 = StaticShape.newBuilder(); - StaticProperty p2 = new DefaultStaticProperty(descriptor.kind); - b2.property(p2, "property", false); + StaticProperty p2 = new DefaultStaticProperty("property", descriptor.kind, false); + b2.property(p2); StaticShape s2 = b2.build(); DefaultStaticObject o2 = s2.getFactory().create(); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index 69dd714552ae..f1049e24025f 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -36,7 +36,6 @@ import com.oracle.truffle.api.impl.asm.MethodVisitor; import com.oracle.truffle.api.impl.asm.Opcodes; import com.oracle.truffle.api.impl.asm.Type; -import com.oracle.truffle.espresso.staticobject.StaticShape.ExtendedProperty; import org.graalvm.collections.Pair; import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_FINAL; @@ -148,8 +147,8 @@ private static int getObjectFieldOffset(Class c, String fieldName) { } @Override - StaticShape generateShape(StaticShape parentShape, Collection extendedProperties) { - return ArrayBasedStaticShape.create(generatedStorageClass, generatedFactoryClass, (ArrayBasedStaticShape) parentShape, extendedProperties, byteArrayOffset, objectArrayOffset, shapeOffset); + StaticShape generateShape(StaticShape parentShape, Collection staticProperties) { + return ArrayBasedStaticShape.create(generatedStorageClass, generatedFactoryClass, (ArrayBasedStaticShape) parentShape, staticProperties, byteArrayOffset, objectArrayOffset, shapeOffset); } private static String getStorageConstructorDescriptor(Constructor superConstructor) { @@ -229,10 +228,10 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, Label lSetPrimitive = new Label(); mv.visitJumpInsn(GOTO, lSetPrimitive); mv.visitLabel(lNoPrimitives); - mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 1, new Object[] {storageName}); + mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 1, new Object[]{storageName}); mv.visitInsn(ACONST_NULL); mv.visitLabel(lSetPrimitive); - mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 2, new Object[] {storageName, "[B"}); + mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 2, new Object[]{storageName, "[B"}); mv.visitFieldInsn(PUTFIELD, storageName, "primitive", "[B"); // object = objectArraySize > 0 ? new Object[objectArraySize] : null; @@ -245,10 +244,10 @@ private static void addStorageConstructors(ClassVisitor cv, String storageName, Label lSetObject = new Label(); mv.visitJumpInsn(GOTO, lSetObject); mv.visitLabel(lNoObjects); - mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 1, new Object[] {storageName}); + mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 1, new Object[]{storageName}); mv.visitInsn(ACONST_NULL); mv.visitLabel(lSetObject); - mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 2, new Object[] {storageName, "[Ljava/lang/Object;"}); + mv.visitFrame(F_FULL, frameLocals.length, frameLocals, 2, new Object[]{storageName, "[Ljava/lang/Object;"}); mv.visitFieldInsn(PUTFIELD, storageName, "object", "[Ljava/lang/Object;"); mv.visitInsn(RETURN); @@ -275,7 +274,7 @@ private static String[] getCloneMethodExceptions(Method cloneMethod) { private static void addCloneMethod(Class storageSuperClass, ClassVisitor cv, String className) { // Prepare array of frame locals for jumps - Object[] frameLocals = new Object[] {className, className}; + Object[] frameLocals = new Object[]{className, className}; Method superCloneMethod = getCloneMethod(storageSuperClass); String superCloneMethodDescriptor = Type.getMethodDescriptor(superCloneMethod); @@ -295,14 +294,14 @@ private static void addCloneMethod(Class storageSuperClass, ClassVisitor cv, Label lSetPrimitive = new Label(); mv.visitJumpInsn(GOTO, lSetPrimitive); mv.visitLabel(lHasPrimitives); - mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 1, new Object[] {className}); + mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 1, new Object[]{className}); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, "primitive", "[B"); mv.visitMethodInsn(INVOKEVIRTUAL, "[B", "clone", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, "[B"); mv.visitTypeInsn(CHECKCAST, "[B"); mv.visitLabel(lSetPrimitive); - mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 2, new Object[] {className, "[B"}); + mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 2, new Object[]{className, "[B"}); mv.visitFieldInsn(PUTFIELD, className, "primitive", "[B"); // clone.object = (object == null ? null : (Object[]) object.clone()); @@ -315,14 +314,14 @@ private static void addCloneMethod(Class storageSuperClass, ClassVisitor cv, Label lSetObject = new Label(); mv.visitJumpInsn(GOTO, lSetObject); mv.visitLabel(lHasObjects); - mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 1, new Object[] {className}); + mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 1, new Object[]{className}); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, "object", "[Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "[Ljava/lang/Object;", "clone", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;"); mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;"); mv.visitLabel(lSetObject); - mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 2, new Object[] {className, "[Ljava/lang/Object;"}); + mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 2, new Object[]{className, "[Ljava/lang/Object;"}); mv.visitFieldInsn(PUTFIELD, className, "object", "[Ljava/lang/Object;"); mv.visitVarInsn(ALOAD, 1); @@ -394,17 +393,17 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl } } - private static Collection generateStorageProperties() { + private static Collection generateStorageProperties() { return Arrays.asList( - new ExtendedProperty(new DefaultStaticProperty(StaticPropertyKind.BYTE_ARRAY), "primitive", true), - new ExtendedProperty(new DefaultStaticProperty(StaticPropertyKind.OBJECT_ARRAY), "object", true), - new ExtendedProperty(new DefaultStaticProperty(StaticPropertyKind.Object), "shape", true)); + new DefaultStaticProperty("primitive", StaticPropertyKind.BYTE_ARRAY, true), + new DefaultStaticProperty("object", StaticPropertyKind.OBJECT_ARRAY, true), + new DefaultStaticProperty("shape", StaticPropertyKind.Object, true)); } private static Class generateStorage(Class storageSuperClass) { String storageSuperName = Type.getInternalName(storageSuperClass); String storageName = generateStorageName(); - Collection arrayProperties = generateStorageProperties(); + Collection arrayProperties = generateStorageProperties(); ClassWriter storageWriter = new ClassWriter(0); int storageAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java index 666924f829e8..31004389cf95 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedStaticShape.java @@ -73,10 +73,10 @@ private static synchronized void initializeShapeChecks() { } static ArrayBasedStaticShape create(Class generatedStorageClass, Class generatedFactoryClass, ArrayBasedStaticShape parentShape, - Collection extendedProperties, int byteArrayOffset, int objectArrayOffset, int shapeOffset) { + Collection staticProperties, int byteArrayOffset, int objectArrayOffset, int shapeOffset) { try { ArrayBasedPropertyLayout parentPropertyLayout = parentShape == null ? null : parentShape.getPropertyLayout(); - ArrayBasedPropertyLayout propertyLayout = new ArrayBasedPropertyLayout(parentPropertyLayout, extendedProperties, byteArrayOffset, objectArrayOffset, shapeOffset); + ArrayBasedPropertyLayout propertyLayout = new ArrayBasedPropertyLayout(parentPropertyLayout, staticProperties, byteArrayOffset, objectArrayOffset, shapeOffset); ArrayBasedStaticShape shape = new ArrayBasedStaticShape<>(parentShape, generatedStorageClass, propertyLayout); T factory = generatedFactoryClass.cast( generatedFactoryClass.getConstructor(Object.class, int.class, int.class).newInstance(shape, propertyLayout.getPrimitiveArraySize(), propertyLayout.getObjectArraySize())); @@ -192,7 +192,7 @@ static class ArrayBasedPropertyLayout { private final int objectArrayOffset; private final int shapeOffset; - ArrayBasedPropertyLayout(ArrayBasedPropertyLayout parentLayout, Collection extendedProperties, int byteArrayOffset, int objectArrayOffset, int shapeOffset) { + ArrayBasedPropertyLayout(ArrayBasedPropertyLayout parentLayout, Collection staticProperties, int byteArrayOffset, int objectArrayOffset, int shapeOffset) { this.byteArrayOffset = byteArrayOffset; this.objectArrayOffset = objectArrayOffset; this.shapeOffset = shapeOffset; @@ -218,23 +218,23 @@ static class ArrayBasedPropertyLayout { } int[] primitiveFields = new int[N_PRIMITIVES]; - for (ExtendedProperty extendedProperty : extendedProperties) { - byte propertyKind = extendedProperty.getProperty().getInternalKind(); + for (StaticProperty staticProperty : staticProperties) { + byte propertyKind = staticProperty.getInternalKind(); if (propertyKind != StaticPropertyKind.Object.toByte()) { primitiveFields[propertyKind]++; } } PrimitiveFieldIndexes primitiveFieldIndexes = new PrimitiveFieldIndexes(primitiveFields, superTotalByteCount, parentLeftoverHoles); - for (ExtendedProperty extendedProperty : extendedProperties) { - byte propertyKind = extendedProperty.getProperty().getInternalKind(); + for (StaticProperty staticProperty : staticProperties) { + byte propertyKind = staticProperty.getInternalKind(); int index; if (propertyKind == StaticPropertyKind.Object.toByte()) { index = Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * objArraySize++; } else { index = primitiveFieldIndexes.getIndex(propertyKind); } - extendedProperty.getProperty().initOffset(index); + staticProperty.initOffset(index); } lastOffset = primitiveFieldIndexes.offsets[N_PRIMITIVES - 1]; primitiveArraySize = getSizeToAlloc(parentLayout == null ? 0 : parentLayout.primitiveArraySize, primitiveFieldIndexes); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java index c655959e065f..7bacb2244eb1 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java @@ -23,11 +23,26 @@ package com.oracle.truffle.espresso.staticobject; public final class DefaultStaticProperty extends StaticProperty { - DefaultStaticProperty(byte internalKind) { + private final String name; + private final boolean isFinal; + + DefaultStaticProperty(String name, byte internalKind, boolean isFinal) { super(internalKind); + this.name = name; + this.isFinal = isFinal; + } + + public DefaultStaticProperty(String name, StaticPropertyKind kind, boolean isFinal) { + this(name, kind.toByte(), isFinal); + } + + @Override + public String getName() { + return name; } - public DefaultStaticProperty(StaticPropertyKind kind) { - super(kind); + @Override + public boolean isFinal() { + return isFinal; } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java index afbfa33f0358..a4c8bf132dfb 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java @@ -26,7 +26,6 @@ import com.oracle.truffle.api.impl.asm.ClassWriter; import com.oracle.truffle.api.impl.asm.MethodVisitor; import com.oracle.truffle.api.impl.asm.Type; -import com.oracle.truffle.espresso.staticobject.StaticShape.ExtendedProperty; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -60,12 +59,12 @@ static FieldBasedShapeGenerator getShapeGenerator(Class storageSuperCl } @Override - StaticShape generateShape(StaticShape parentShape, Collection extendedProperties) { - Class generatedStorageClass = generateStorage(storageSuperClass, extendedProperties); + StaticShape generateShape(StaticShape parentShape, Collection staticProperties) { + Class generatedStorageClass = generateStorage(storageSuperClass, staticProperties); Class generatedFactoryClass = generateFactory(generatedStorageClass, storageFactoryInterface); - for (ExtendedProperty extendedProperty : extendedProperties) { - int offset = getObjectFieldOffset(generatedStorageClass, extendedProperty.getName()); - extendedProperty.getProperty().initOffset(offset); + for (StaticProperty staticProperty : staticProperties) { + int offset = getObjectFieldOffset(generatedStorageClass, staticProperty.getName()); + staticProperty.initOffset(offset); } return FieldBasedStaticShape.create(generatedStorageClass, generatedFactoryClass); } @@ -136,14 +135,14 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl } } - private static Class generateStorage(Class storageSuperClass, Collection extendedProperties) { + private static Class generateStorage(Class storageSuperClass, Collection staticProperties) { String storageSuperName = Type.getInternalName(storageSuperClass); String storageName = generateStorageName(); ClassWriter storageWriter = new ClassWriter(0); int storageAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC; storageWriter.visit(V1_8, storageAccess, storageName, null, storageSuperName, null); addStorageConstructors(storageWriter, storageSuperClass, storageSuperName); - addStorageFields(storageWriter, extendedProperties); + addStorageFields(storageWriter, staticProperties); storageWriter.visitEnd(); return load(storageName, storageWriter.toByteArray(), storageSuperClass); } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 46b9898ab8b7..5bacb6ed6a49 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -25,7 +25,6 @@ import com.oracle.truffle.api.impl.asm.ClassVisitor; import com.oracle.truffle.api.impl.asm.FieldVisitor; import com.oracle.truffle.api.impl.asm.Type; -import com.oracle.truffle.espresso.staticobject.StaticShape.ExtendedProperty; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; @@ -65,7 +64,7 @@ abstract class ShapeGenerator { } } - abstract StaticShape generateShape(StaticShape parentShape, Collection extendedProperties); + abstract StaticShape generateShape(StaticShape parentShape, Collection staticProperties); static ShapeGenerator getShapeGenerator(StaticShape parentShape) { Class parentStorageClass = parentShape.getStorageClass(); @@ -89,13 +88,13 @@ static String generateFactoryName(Class generatedStorageClass) { return Type.getInternalName(generatedStorageClass) + DELIMITER + "Factory"; } - static void addStorageFields(ClassVisitor cv, Collection extendedProperties) { - for (ExtendedProperty extendedProperty : extendedProperties) { + static void addStorageFields(ClassVisitor cv, Collection staticProperties) { + for (StaticProperty staticProperty : staticProperties) { int access = ACC_PUBLIC; - if (extendedProperty.isFinal()) { + if (staticProperty.isFinal()) { access |= ACC_FINAL; } - FieldVisitor fv = cv.visitField(access, extendedProperty.getName(), StaticPropertyKind.getDescriptor(extendedProperty.getProperty().getInternalKind()), null, null); + FieldVisitor fv = cv.visitField(access, staticProperty.getName(), StaticPropertyKind.getDescriptor(staticProperty.getInternalKind()), null, null); fv.visitEnd(); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index c1419a7d90c9..32faeec5a6f8 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -44,20 +44,26 @@ protected StaticProperty(StaticPropertyKind kind) { this(getInternalKind(kind)); } + public abstract String getName(); + + public abstract boolean isFinal(); + private static byte getInternalKind(StaticPropertyKind kind) { return kind.toByte(); } final void initOffset(int o) { if (this.offset != 0) { - throw new RuntimeException("Attempt to reinitialize the offset of a static property. Was it added to more than one builder or multiple times to the same builder?"); + throw new RuntimeException("Attempt to reinitialize the offset of static property '" + getName() + "' of kind '" + StaticPropertyKind.valueOf(internalKind).name() + "'.\n" + + "Was it added to more than one builder or multiple times to the same builder?"); } this.offset = o; } final void initShape(StaticShape s) { if (this.shape != null) { - throw new RuntimeException("Attempt to reinitialize the shape of a static property. Was it added to more than one builder or multiple times to the same builder?"); + throw new RuntimeException("Attempt to reinitialize the shape of static property '" + getName() + "' of kind '" + StaticPropertyKind.valueOf(internalKind).name() + "'.\n" + + "Was it added to more than one builder or multiple times to the same builder?"); } this.shape = s; } @@ -70,7 +76,7 @@ private void checkKind(StaticPropertyKind kind) { if (this.internalKind != getInternalKind(kind)) { CompilerDirectives.transferToInterpreterAndInvalidate(); String kindName = StaticPropertyKind.valueOf(internalKind).name(); - throw new RuntimeException("Static property of '" + kindName + "' kind cannot be accessed as '" + kind.name() + "'"); + throw new RuntimeException("Static property '" + getName() + "' of kind '" + kindName + "' cannot be accessed as '" + kind.name() + "'"); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 6f7d81b22d7d..81ca53013e12 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -29,7 +29,6 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Objects; @@ -91,18 +90,18 @@ private static Unsafe getUnsafe() { } public static final class Builder { - private final HashMap extendedProperties = new LinkedHashMap<>(); + private final HashMap staticProperties = new LinkedHashMap<>(); Builder() { } - public Builder property(StaticProperty property, String name, boolean isFinal) { + public Builder property(StaticProperty property) { CompilerAsserts.neverPartOfCompilation(); Objects.requireNonNull(property); - if (extendedProperties.containsKey(name)) { - throw new IllegalArgumentException("This builder already contains a property named '" + name + "'"); + if (staticProperties.containsKey(property.getName())) { + throw new IllegalArgumentException("This builder already contains a property named '" + property.getName() + "'"); } - extendedProperties.put(name, new ExtendedProperty(property, name, isFinal)); + staticProperties.put(property.getName(), property); return this; } @@ -127,9 +126,9 @@ public StaticShape build(Class superClass, Class factoryInterface) private StaticShape build(ShapeGenerator sg, StaticShape parentShape) { CompilerAsserts.neverPartOfCompilation(); - StaticShape shape = sg.generateShape(parentShape, extendedProperties.values()); - for (ExtendedProperty extendedProperty : extendedProperties.values()) { - extendedProperty.property.initShape(shape); + StaticShape shape = sg.generateShape(parentShape, staticProperties.values()); + for (StaticProperty staticProperty : staticProperties.values()) { + staticProperty.initShape(shape); } return shape; } @@ -153,30 +152,6 @@ private static void validate(Class storageFactoryInterface, Class storageS } } - static final class ExtendedProperty { - private final StaticProperty property; - private final String name; - private final boolean isFinal; - - ExtendedProperty(StaticProperty property, String name, boolean isFinal) { - this.property = property; - this.name = name; - this.isFinal = isFinal; - } - - StaticProperty getProperty() { - return property; - } - - String getName() { - return name; - } - - boolean isFinal() { - return isFinal; - } - } - abstract static class PrivilegedToken { PrivilegedToken() { if (!isKnownImplementation()) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java index c3aad6ec6d17..7f7c95e0a3ed 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java @@ -55,7 +55,7 @@ public final class ArrayKlass extends Klass { private final Klass elementalType; private final int dimension; - private static final StaticProperty ARRAY_PROPERTY = new DefaultStaticProperty(StaticPropertyKind.Object); + private static final StaticProperty ARRAY_PROPERTY = new DefaultStaticProperty("array", StaticPropertyKind.Object, true); // This field should be static final, but until we move the static object model we cannot have a // SubstrateVM feature which will allow us to set the right field offsets at image build time. @CompilationFinal // @@ -89,7 +89,7 @@ public static StaticShape getArrayShape() { @TruffleBoundary private static synchronized void initializeArrayShape() { if (arrayShape == null) { - arrayShape = StaticShape.newBuilder().property(ARRAY_PROPERTY, "array", true).build(StaticObject.class, StaticObjectFactory.class); + arrayShape = StaticShape.newBuilder().property(ARRAY_PROPERTY).build(StaticObject.class, StaticObjectFactory.class); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java index ddd5ddf56d4d..a1908cf7a773 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java @@ -54,7 +54,7 @@ public final class Field extends Member implements FieldRef { @CompilationFinal private Symbol genericSignature = null; public Field(ObjectKlass holder, LinkedField linkedField, boolean hidden) { - super(hidden ? null : linkedField.getType(), linkedField.getName()); + super(hidden ? null : linkedField.getType(), linkedField.getSymbolicName()); this.linkedField = linkedField; this.holder = holder; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index 31a59ddd93d5..a98259e885e5 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -471,7 +471,7 @@ public int compare(Klass k1, Klass k2) { static final DebugCounter KLASS_LOOKUP_DECLARED_METHOD_COUNT = DebugCounter.create("Klass.lookupDeclaredMethod call count"); static final DebugCounter KLASS_LOOKUP_DECLARED_FIELD_COUNT = DebugCounter.create("Klass.lookupDeclaredField call count"); - private static final StaticProperty FOREIGN_PROPERTY = new DefaultStaticProperty(StaticPropertyKind.Object); + private static final StaticProperty FOREIGN_PROPERTY = new DefaultStaticProperty("foreignObject", StaticPropertyKind.Object, true); // This field should be static final, but until we move the static object model we cannot have a // SubstrateVM feature which will allow us to set the right field offsets at image build time. @CompilationFinal // @@ -624,7 +624,7 @@ public static StaticShape getForeignShape() { @TruffleBoundary private static synchronized void initializeForeignShape() { if (foreignShape == null) { - foreignShape = StaticShape.newBuilder().property(FOREIGN_PROPERTY, "foreignObject", true).build(StaticObject.class, StaticObjectFactory.class); + foreignShape = StaticShape.newBuilder().property(FOREIGN_PROPERTY).build(StaticObject.class, StaticObjectFactory.class); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java index 4294d1f388a3..7790eaff1db7 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java @@ -43,6 +43,16 @@ public static LinkedField createHidden(Symbol name, int slot) { return new LinkedField(new ParserField(ParserField.HIDDEN, name, Type.java_lang_Object, null), slot); } + @Override + public String getName() { + return getSymbolicName().toString(); + } + + @Override + public boolean isFinal() { + return parserField.isFinal(); + } + /** * The slot is the position in the `fieldTable` of the ObjectKlass. */ @@ -54,7 +64,7 @@ public Symbol getType() { return parserField.getType(); } - public Symbol getName() { + public Symbol getSymbolicName() { return parserField.getName(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java index 85b012698ece..273b3ec1e4d1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java @@ -59,18 +59,18 @@ final class LinkedKlassFieldLayout { for (ParserField parserField : parserKlass.getFields()) { if (parserField.isStatic()) { LinkedField field = new LinkedField(parserField, nextStaticFieldSlot++); - staticBuilder.property(field, parserField.getName().toString(), parserField.isFinal()); + staticBuilder.property(field); staticFields[nextStaticFieldIndex++] = field; } else { LinkedField field = new LinkedField(parserField, nextInstanceFieldSlot++); - instanceBuilder.property(field, parserField.getName().toString(), parserField.isFinal()); + instanceBuilder.property(field); instanceFields[nextInstanceFieldIndex++] = field; } } for (Symbol hiddenFieldName : fieldCounter.hiddenFieldNames) { ParserField hiddenParserField = new ParserField(ParserField.HIDDEN, hiddenFieldName, Type.java_lang_Object, null); LinkedField field = new LinkedField(hiddenParserField, nextInstanceFieldSlot++); - instanceBuilder.property(field, hiddenFieldName.toString(), false); + instanceBuilder.property(field); instanceFields[nextInstanceFieldIndex++] = field; } if (superKlass == null) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index ff07c2f97db8..f10a77dc48f4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1112,7 +1112,7 @@ public void redefineClass(ChangePacket packet, List refreshSubClass Field oldField = fieldTable[i]; if (outerField == oldField) { for (LinkedField instanceField : instanceFields) { - if (instanceField.getName().equals(outerField.getName())) { + if (instanceField.getSymbolicName().equals(outerField.getName())) { // replace with new field fieldTable[i] = new Field(this, instanceField, false); } From ded768ed9f7ff7c7590c4542720c79c0aaa15eed Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 11 May 2021 18:24:32 +0200 Subject: [PATCH 228/290] Simplify code. --- .../staticobject/ArrayBasedShapeGenerator.java | 12 +++--------- .../staticobject/DefaultStaticProperty.java | 8 ++------ .../espresso/staticobject/ShapeGenerator.java | 13 +++++++------ .../espresso/staticobject/StaticProperty.java | 6 +----- 4 files changed, 13 insertions(+), 26 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java index f1049e24025f..e4e0b693fcbc 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ArrayBasedShapeGenerator.java @@ -393,23 +393,17 @@ private static void addFactoryMethods(ClassVisitor cv, Class storageClass, Cl } } - private static Collection generateStorageProperties() { - return Arrays.asList( - new DefaultStaticProperty("primitive", StaticPropertyKind.BYTE_ARRAY, true), - new DefaultStaticProperty("object", StaticPropertyKind.OBJECT_ARRAY, true), - new DefaultStaticProperty("shape", StaticPropertyKind.Object, true)); - } - private static Class generateStorage(Class storageSuperClass) { String storageSuperName = Type.getInternalName(storageSuperClass); String storageName = generateStorageName(); - Collection arrayProperties = generateStorageProperties(); ClassWriter storageWriter = new ClassWriter(0); int storageAccess = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC; storageWriter.visit(V1_8, storageAccess, storageName, null, storageSuperName, null); addStorageConstructors(storageWriter, storageName, storageSuperClass, storageSuperName); - addStorageFields(storageWriter, arrayProperties); + addStorageField(storageWriter, "primitive", StaticPropertyKind.BYTE_ARRAY, true); + addStorageField(storageWriter, "object", StaticPropertyKind.OBJECT_ARRAY, true); + addStorageField(storageWriter, "shape", StaticPropertyKind.Object.toByte(), true); if (Cloneable.class.isAssignableFrom(storageSuperClass)) { addCloneMethod(storageSuperClass, storageWriter, storageName); } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java index 7bacb2244eb1..25840b0fe961 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java @@ -26,16 +26,12 @@ public final class DefaultStaticProperty extends StaticProperty { private final String name; private final boolean isFinal; - DefaultStaticProperty(String name, byte internalKind, boolean isFinal) { - super(internalKind); + public DefaultStaticProperty(String name, StaticPropertyKind kind, boolean isFinal) { + super(kind); this.name = name; this.isFinal = isFinal; } - public DefaultStaticProperty(String name, StaticPropertyKind kind, boolean isFinal) { - this(name, kind.toByte(), isFinal); - } - @Override public String getName() { return name; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 5bacb6ed6a49..0d159fd5b4e8 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -90,15 +90,16 @@ static String generateFactoryName(Class generatedStorageClass) { static void addStorageFields(ClassVisitor cv, Collection staticProperties) { for (StaticProperty staticProperty : staticProperties) { - int access = ACC_PUBLIC; - if (staticProperty.isFinal()) { - access |= ACC_FINAL; - } - FieldVisitor fv = cv.visitField(access, staticProperty.getName(), StaticPropertyKind.getDescriptor(staticProperty.getInternalKind()), null, null); - fv.visitEnd(); + addStorageField(cv, staticProperty.getName(), staticProperty.getInternalKind(), staticProperty.isFinal()); } } + static void addStorageField(ClassVisitor cv, String propertyName, byte internalKind, boolean isFinal) { + int access = isFinal ? ACC_FINAL | ACC_PUBLIC : ACC_PUBLIC; + FieldVisitor fv = cv.visitField(access, propertyName, StaticPropertyKind.getDescriptor(internalKind), null, null); + fv.visitEnd(); + } + @SuppressWarnings("unchecked") static Class load(String name, byte[] bytes, Class referenceClass) { Object clazz; diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 32faeec5a6f8..92010827a510 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -36,12 +36,8 @@ public abstract class StaticProperty { @CompilationFinal // private int offset; - StaticProperty(byte internalKind) { - this.internalKind = internalKind; - } - protected StaticProperty(StaticPropertyKind kind) { - this(getInternalKind(kind)); + this.internalKind = getInternalKind(kind); } public abstract String getName(); From 12c8a6ae5fb51e58dbc7b72be705626827405149 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Tue, 11 May 2021 19:42:27 +0200 Subject: [PATCH 229/290] Change handling of property name and isFinal. --- .../test/BuilderPropertyTest.java | 58 ++++++++----------- .../staticobject/test/InheritanceTest.java | 15 +++-- .../staticobject/test/PropertyAccessTest.java | 4 +- ...orageLayout.java => StaticObjectTest.java} | 11 +++- .../staticobject/DefaultStaticProperty.java | 11 +--- .../FieldBasedShapeGenerator.java | 2 +- .../espresso/staticobject/ShapeGenerator.java | 6 +- .../espresso/staticobject/StaticProperty.java | 32 +++++----- .../espresso/staticobject/StaticShape.java | 11 ++-- .../oracle/truffle/espresso/impl/Field.java | 2 +- .../truffle/espresso/impl/LinkedField.java | 21 +++---- .../truffle/espresso/impl/ObjectKlass.java | 2 +- 12 files changed, 84 insertions(+), 91 deletions(-) rename espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/{StorageLayout.java => StaticObjectTest.java} (76%) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java index 8b0a26c9a1cc..55449d62e8d2 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -34,20 +34,14 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -public class BuilderPropertyTest { +public class BuilderPropertyTest extends StaticObjectTest { @Test public void sameBuilderSameProperty() { StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); builder.property(property); - try { - // You cannot add the same property twice - builder.property(property); - Assert.fail(); - } catch (IllegalArgumentException e) { - // You cannot add the same property twice - Assert.assertEquals("This builder already contains a property named 'property'", e.getMessage()); - } + builder.property(property); + builder.build(); } @Test @@ -56,13 +50,8 @@ public void sameBuilderSameName() throws IllegalArgumentException { StaticProperty p1 = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); StaticProperty p2 = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); builder.property(p1); - try { - // You cannot add two properties with the same name - builder.property(p2); - Assert.fail(); - } catch (IllegalArgumentException e) { - Assert.assertEquals("This builder already contains a property named 'property'", e.getMessage()); - } + builder.property(p2); + builder.build(); } @Test @@ -85,50 +74,49 @@ public void differentBuildersSameProperty() { @Test public void propertyName() throws NoSuchFieldException { - Assume.assumeFalse(StorageLayout.ARRAY_BASED); + Assume.assumeFalse(ARRAY_BASED); StaticShape.Builder builder = StaticShape.newBuilder(); - String propertyName = "property"; - StaticProperty property = new DefaultStaticProperty(propertyName, StaticPropertyKind.Int, false); + StaticProperty property = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); builder.property(property); StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); - Field field = object.getClass().getField(propertyName); - Assert.assertEquals(propertyName, field.getName()); + object.getClass().getField(guessGeneratedFieldName(property)); } @Test public void propertyFinal() throws NoSuchFieldException { - Assume.assumeFalse(StorageLayout.ARRAY_BASED); + Assume.assumeFalse(ARRAY_BASED); StaticShape.Builder builder = StaticShape.newBuilder(); - String p1Name = "p1"; - String p2Name = "p2"; - StaticProperty p1 = new DefaultStaticProperty(p1Name, StaticPropertyKind.Int, true); - StaticProperty p2 = new DefaultStaticProperty(p2Name, StaticPropertyKind.Int, false); + StaticProperty p1 = new DefaultStaticProperty("p1", StaticPropertyKind.Int, true); + StaticProperty p2 = new DefaultStaticProperty("p2", StaticPropertyKind.Int, false); builder.property(p1); builder.property(p2); StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); - Field f1 = object.getClass().getField(p1Name); - Field f2 = object.getClass().getField(p2Name); + Field f1 = object.getClass().getField(guessGeneratedFieldName(p1)); + Field f2 = object.getClass().getField(guessGeneratedFieldName(p2)); Assert.assertTrue(Modifier.isFinal(f1.getModifiers())); Assert.assertFalse(Modifier.isFinal(f2.getModifiers())); } @Test public void propertyKind() throws NoSuchFieldException { - Assume.assumeFalse(StorageLayout.ARRAY_BASED); + Assume.assumeFalse(ARRAY_BASED); StaticShape.Builder builder = StaticShape.newBuilder(); - for (StaticPropertyKind kind : StaticPropertyKind.values()) { - builder.property(new DefaultStaticProperty(kind.name(), kind, false)); + StaticPropertyKind[] kinds = StaticPropertyKind.values(); + StaticProperty[] properties = new StaticProperty[kinds.length]; + for (int i = 0; i < properties.length; i++) { + properties[i] = new DefaultStaticProperty(kinds[i].name(), kinds[i], false); + builder.property(properties[i]); } StaticShape shape = builder.build(); DefaultStaticObject object = shape.getFactory().create(); - for (StaticPropertyKind kind : StaticPropertyKind.values()) { + for (int i = 0; i < properties.length; i++) { Class expectedType; - switch (kind) { + switch (kinds[i]) { case Boolean: expectedType = boolean.class; break; @@ -158,9 +146,9 @@ public void propertyKind() throws NoSuchFieldException { break; default: expectedType = null; - Assert.fail("Unexpected type: " + kind); + Assert.fail("Unexpected type: " + kinds[i]); } - Assert.assertEquals(expectedType, object.getClass().getField(kind.name()).getType()); + Assert.assertEquals(expectedType, object.getClass().getField(guessGeneratedFieldName(properties[i])).getType()); } } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java index df7f4014da83..6d01150349e3 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java @@ -31,7 +31,7 @@ import org.junit.Assume; import org.junit.Test; -public class InheritanceTest { +public class InheritanceTest extends StaticObjectTest { public static class CustomStaticObject { public byte field1; public boolean field2; @@ -58,11 +58,13 @@ public void baseClassInheritance() throws NoSuchFieldException { // Get the value of the field declared in the generated class Assert.assertEquals(24, property.getInt(object)); - Assume.assumeFalse(StorageLayout.ARRAY_BASED); - // `CustomStaticObject.field1` is shadowed - Assert.assertEquals(int.class, object.getClass().getField("field1").getType()); + Assume.assumeFalse(ARRAY_BASED); + // `CustomStaticObject.field1` is still visible + Assert.assertEquals(byte.class, object.getClass().getField("field1").getType()); // `CustomStaticObject.field2` is visible Assert.assertEquals(boolean.class, object.getClass().getField("field2").getType()); + // The generated field is accessible + Assert.assertEquals(int.class, object.getClass().getField(guessGeneratedFieldName(property)).getType()); } @Test @@ -95,7 +97,8 @@ public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessExc Assert.assertEquals(1, s1p1.getInt(object)); Assert.assertEquals(3, s2p1.getInt(object)); - Assume.assumeFalse(StorageLayout.ARRAY_BASED); - Assert.assertEquals(3, object.getClass().getField("field1").getInt(object)); + Assume.assumeFalse(ARRAY_BASED); + Assert.assertEquals(1, object.getClass().getField(guessGeneratedFieldName(s1p1)).getInt(object)); + Assert.assertEquals(3, object.getClass().getField(guessGeneratedFieldName(s2p1)).getInt(object)); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java index ade9ca960c5d..a8885d8a4ac9 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/PropertyAccessTest.java @@ -37,7 +37,7 @@ import org.junit.runner.RunWith; @RunWith(Theories.class) -public class PropertyAccessTest { +public class PropertyAccessTest extends StaticObjectTest { @DataPoints // public static TestDescriptor[] descriptors; @@ -218,7 +218,7 @@ public void wrongShape(TestDescriptor descriptor) { descriptor.setter.set(p1, o2, descriptor.testValue); Assert.fail(); } catch (ClassCastException e) { - Assert.assertTrue(!StorageLayout.ARRAY_BASED); + Assert.assertTrue(!ARRAY_BASED); } catch (RuntimeException e) { Assert.assertTrue(e.getMessage().startsWith("Incompatible shape on property access.")); } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StaticObjectTest.java similarity index 76% rename from espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java rename to espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StaticObjectTest.java index fcebfb2a0989..a4c1e95284af 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StorageLayout.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StaticObjectTest.java @@ -22,6 +22,15 @@ */ package com.oracle.truffle.espresso.staticobject.test; -class StorageLayout { +import com.oracle.truffle.espresso.staticobject.StaticProperty; + +class StaticObjectTest { static final boolean ARRAY_BASED = !Boolean.getBoolean("com.oracle.truffle.espresso.staticobject.FieldBasedStorage"); + + String guessGeneratedFieldName(StaticProperty property) { + assert !ARRAY_BASED; + // The format of generated field names with the field-based storage might change at any + // time. Do not depend on it! + return property + "@" + System.identityHashCode(property); + } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java index 25840b0fe961..c44a74519a20 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java @@ -24,21 +24,14 @@ public final class DefaultStaticProperty extends StaticProperty { private final String name; - private final boolean isFinal; public DefaultStaticProperty(String name, StaticPropertyKind kind, boolean isFinal) { - super(kind); + super(kind, isFinal); this.name = name; - this.isFinal = isFinal; } @Override - public String getName() { + public String toString() { return name; } - - @Override - public boolean isFinal() { - return isFinal; - } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java index a4c8bf132dfb..87bed447979b 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/FieldBasedShapeGenerator.java @@ -63,7 +63,7 @@ StaticShape generateShape(StaticShape parentShape, Collection generatedStorageClass = generateStorage(storageSuperClass, staticProperties); Class generatedFactoryClass = generateFactory(generatedStorageClass, storageFactoryInterface); for (StaticProperty staticProperty : staticProperties) { - int offset = getObjectFieldOffset(generatedStorageClass, staticProperty.getName()); + int offset = getObjectFieldOffset(generatedStorageClass, generateFieldName(staticProperty)); staticProperty.initOffset(offset); } return FieldBasedStaticShape.create(generatedStorageClass, generatedFactoryClass); diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 0d159fd5b4e8..9c8ded8c145b 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -88,9 +88,13 @@ static String generateFactoryName(Class generatedStorageClass) { return Type.getInternalName(generatedStorageClass) + DELIMITER + "Factory"; } + static String generateFieldName(StaticProperty property) { + return property.toString() + "@" + System.identityHashCode(property); + } + static void addStorageFields(ClassVisitor cv, Collection staticProperties) { for (StaticProperty staticProperty : staticProperties) { - addStorageField(cv, staticProperty.getName(), staticProperty.getInternalKind(), staticProperty.isFinal()); + addStorageField(cv, generateFieldName(staticProperty), staticProperty.getInternalKind(), staticProperty.isFinal()); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 92010827a510..92a8108039fc 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -29,28 +29,35 @@ public abstract class StaticProperty { private static final Unsafe UNSAFE = getUnsafe(); - private final byte internalKind; + private static final byte IS_FINAL = (byte) (1 << 7); + private final byte flags; @CompilationFinal // private StaticShape shape; // The offset is the actual position in the field array of an actual instance. @CompilationFinal // private int offset; - protected StaticProperty(StaticPropertyKind kind) { - this.internalKind = getInternalKind(kind); + protected StaticProperty(StaticPropertyKind kind, boolean isFinal) { + byte internalKind = getInternalKind(kind); + assert (internalKind & IS_FINAL) == 0; + this.flags = (byte) (isFinal ? IS_FINAL | internalKind : internalKind); } - public abstract String getName(); - - public abstract boolean isFinal(); + public final boolean isFinal() { + return (flags & IS_FINAL) == IS_FINAL; + } private static byte getInternalKind(StaticPropertyKind kind) { return kind.toByte(); } + final byte getInternalKind() { + return (byte) (flags & ~IS_FINAL); + } + final void initOffset(int o) { if (this.offset != 0) { - throw new RuntimeException("Attempt to reinitialize the offset of static property '" + getName() + "' of kind '" + StaticPropertyKind.valueOf(internalKind).name() + "'.\n" + + throw new RuntimeException("Attempt to reinitialize the offset of static property '" + this + "' of kind '" + StaticPropertyKind.valueOf(getInternalKind()).name() + "'.\n" + "Was it added to more than one builder or multiple times to the same builder?"); } this.offset = o; @@ -58,21 +65,18 @@ final void initOffset(int o) { final void initShape(StaticShape s) { if (this.shape != null) { - throw new RuntimeException("Attempt to reinitialize the shape of static property '" + getName() + "' of kind '" + StaticPropertyKind.valueOf(internalKind).name() + "'.\n" + + throw new RuntimeException("Attempt to reinitialize the shape of static property '" + this + "' of kind '" + StaticPropertyKind.valueOf(getInternalKind()).name() + "'.\n" + "Was it added to more than one builder or multiple times to the same builder?"); } this.shape = s; } - final byte getInternalKind() { - return internalKind; - } - private void checkKind(StaticPropertyKind kind) { - if (this.internalKind != getInternalKind(kind)) { + byte internalKind = getInternalKind(); + if (internalKind != getInternalKind(kind)) { CompilerDirectives.transferToInterpreterAndInvalidate(); String kindName = StaticPropertyKind.valueOf(internalKind).name(); - throw new RuntimeException("Static property '" + getName() + "' of kind '" + kindName + "' cannot be accessed as '" + kind.name() + "'"); + throw new RuntimeException("Static property '" + this + "' of kind '" + kindName + "' cannot be accessed as '" + kind.name() + "'"); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 81ca53013e12..48e494548067 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -90,7 +90,7 @@ private static Unsafe getUnsafe() { } public static final class Builder { - private final HashMap staticProperties = new LinkedHashMap<>(); + private final HashMap staticProperties = new LinkedHashMap<>(); Builder() { } @@ -98,10 +98,7 @@ public static final class Builder { public Builder property(StaticProperty property) { CompilerAsserts.neverPartOfCompilation(); Objects.requireNonNull(property); - if (staticProperties.containsKey(property.getName())) { - throw new IllegalArgumentException("This builder already contains a property named '" + property.getName() + "'"); - } - staticProperties.put(property.getName(), property); + staticProperties.put(property, property); return this; } @@ -140,13 +137,13 @@ private static void validate(Class storageFactoryInterface, Class storageS } for (Method m : storageFactoryInterface.getMethods()) { if (!m.getReturnType().isAssignableFrom(storageSuperClass)) { - throw new RuntimeException("The return type of '" + m.getReturnType().getName() + " " + storageFactoryInterface.getName() + "." + m.toString() + "' is not assignable from '" + + throw new RuntimeException("The return type of '" + m.getReturnType().getName() + " " + storageFactoryInterface.getName() + "." + m + "' is not assignable from '" + storageSuperClass.getName() + "'"); } try { storageSuperClass.getDeclaredConstructor(m.getParameterTypes()); } catch (NoSuchMethodException e) { - throw new RuntimeException("Method '" + m.toString() + "' does not match any constructor in '" + storageSuperClass.getName() + "'", e); + throw new RuntimeException("Method '" + m + "' does not match any constructor in '" + storageSuperClass.getName() + "'", e); } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java index a1908cf7a773..ddd5ddf56d4d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java @@ -54,7 +54,7 @@ public final class Field extends Member implements FieldRef { @CompilationFinal private Symbol genericSignature = null; public Field(ObjectKlass holder, LinkedField linkedField, boolean hidden) { - super(hidden ? null : linkedField.getType(), linkedField.getSymbolicName()); + super(hidden ? null : linkedField.getType(), linkedField.getName()); this.linkedField = linkedField; this.holder = holder; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java index 7790eaff1db7..9b5faabe7672 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java @@ -34,7 +34,7 @@ final class LinkedField extends StaticProperty { private final int slot; LinkedField(ParserField parserField, int slot) { - super(parserField.getPropertyKind()); + super(parserField.getPropertyKind(), parserField.isFinal()); this.parserField = parserField; this.slot = slot; } @@ -43,14 +43,8 @@ public static LinkedField createHidden(Symbol name, int slot) { return new LinkedField(new ParserField(ParserField.HIDDEN, name, Type.java_lang_Object, null), slot); } - @Override - public String getName() { - return getSymbolicName().toString(); - } - - @Override - public boolean isFinal() { - return parserField.isFinal(); + public Symbol getName() { + return parserField.getName(); } /** @@ -64,10 +58,6 @@ public Symbol getType() { return parserField.getType(); } - public Symbol getSymbolicName() { - return parserField.getName(); - } - public int getFlags() { return parserField.getFlags(); } @@ -89,6 +79,11 @@ public boolean isHidden() { return parserField.isHidden(); } + @Override + public String toString() { + return getName().toString(); + } + ParserField getParserField() { return parserField; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index f10a77dc48f4..ff07c2f97db8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1112,7 +1112,7 @@ public void redefineClass(ChangePacket packet, List refreshSubClass Field oldField = fieldTable[i]; if (outerField == oldField) { for (LinkedField instanceField : instanceFields) { - if (instanceField.getSymbolicName().equals(outerField.getName())) { + if (instanceField.getName().equals(outerField.getName())) { // replace with new field fieldTable[i] = new Field(this, instanceField, false); } From 0dfab9a16c188524096db3485bfc8df8ff6a4092 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 12 May 2021 12:27:18 +0200 Subject: [PATCH 230/290] Align builds list entries. --- espresso/ci.jsonnet | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/espresso/ci.jsonnet b/espresso/ci.jsonnet index f088313cfb8f..60105d8838fc 100644 --- a/espresso/ci.jsonnet +++ b/espresso/ci.jsonnet @@ -35,28 +35,28 @@ builds: common.builds + [ // Benchmarks // AWFY peak perf. benchmarks - common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*', guest_jvm_config='field-based', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-field-based-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*' , extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*' , guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('jvm-ce', 'awfy:*' , guest_jvm_config='field-based' , extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-jvm-ce-awfy-field-based-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*' , extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_benchmark('native-ce', 'awfy:*', guest_jvm_config='no-shape-checks', extra_args=['--vm.Xmx1g', '--vm.Xms1g']) + {name: 'weekly-bench-espresso-native-ce-awfy-no-shape-checks-jdk8-linux-amd64'}, // AWFY interpreter benchmarks - common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('jvm-ce', 'awfy:*') + {name: 'weekly-bench-espresso-jvm-ce-awfy_interpreter-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('native-ce', 'awfy:*') + {name: 'weekly-bench-espresso-native-ce-awfy_interpreter-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('jvm-ce', 'awfy:*') + {name: 'weekly-bench-espresso-jvm-ce-awfy_interpreter-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.espresso_interpreter_benchmark('native-ce', 'awfy:*') + {name: 'weekly-bench-espresso-native-ce-awfy_interpreter-jdk8-linux-amd64'}, // Scala DaCapo warmup benchmarks - common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('jvm-ce', 'single-tier', extra_args=['--vm.XX:ReservedCodeCacheSize=1g']) + {name: 'weekly-bench-espresso-jvm-ce-scala_dacapo_warmup-single_tier-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('native-ce', 'single-tier') + {name: 'weekly-bench-espresso-native-ce-scala_dacapo_warmup-single_tier-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('jvm-ce', 'multi-tier', extra_args=['--vm.XX:ReservedCodeCacheSize=1g']) + {name: 'weekly-bench-espresso-jvm-ce-scala_dacapo_warmup-multi_tier-jdk8-linux-amd64'}, - common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('native-ce', 'multi-tier') + {name: 'weekly-bench-espresso-native-ce-scala_dacapo_warmup-multi_tier-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('jvm-ce', 'single-tier' , extra_args=['--vm.XX:ReservedCodeCacheSize=1g']) + {name: 'weekly-bench-espresso-jvm-ce-scala_dacapo_warmup-single_tier-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('native-ce', 'single-tier') + {name: 'weekly-bench-espresso-native-ce-scala_dacapo_warmup-single_tier-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('jvm-ce', 'multi-tier' , extra_args=['--vm.XX:ReservedCodeCacheSize=1g']) + {name: 'weekly-bench-espresso-jvm-ce-scala_dacapo_warmup-multi_tier-jdk8-linux-amd64'}, + common.jdk8_weekly_bench_linux + common.scala_dacapo_warmup_benchmark('native-ce', 'multi-tier') + {name: 'weekly-bench-espresso-native-ce-scala_dacapo_warmup-multi_tier-jdk8-linux-amd64'}, // On-demand benchmarks // Scala DaCapo warmup benchmarks - common.jdk8_on_demand_bench_linux + common.graal_benchmark('jvm-ce', common.scala_dacapo_jvm_fast(warmup=true)) + {name: 'ondemand-bench-espresso-jvm-ce-scala_dacapo_warmup-jdk8-linux-amd64'}, + common.jdk8_on_demand_bench_linux + common.graal_benchmark('jvm-ce', common.scala_dacapo_jvm_fast(warmup=true)) + {name: 'ondemand-bench-espresso-jvm-ce-scala_dacapo_warmup-jdk8-linux-amd64'}, // Memory footprint - common.jdk8_on_demand_linux + common.espresso_minheap_benchmark('jvm-ce', 'awfy:*', 'infinite-overhead') + {name: 'ondemand-bench-espresso-jvm-ce-awfy-minheap-infinite-ovh-jdk8-linux-amd64'}, - common.jdk8_on_demand_bench_linux + common.espresso_minheap_benchmark('jvm-ce', 'awfy:*', '1.5-overhead') + {name: 'ondemand-bench-espresso-jvm-ce-awfy-minheap-1.5-ovh-jdk8-linux-amd64'}, + common.jdk8_on_demand_linux + common.espresso_minheap_benchmark('jvm-ce', 'awfy:*', 'infinite-overhead') + {name: 'ondemand-bench-espresso-jvm-ce-awfy-minheap-infinite-ovh-jdk8-linux-amd64'}, + common.jdk8_on_demand_bench_linux + common.espresso_minheap_benchmark('jvm-ce', 'awfy:*', '1.5-overhead') + {name: 'ondemand-bench-espresso-jvm-ce-awfy-minheap-1.5-ovh-jdk8-linux-amd64'}, ] } From cef64c5d0eff864c9f034bf32df288a6bfa67ae4 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 12 May 2021 13:20:54 +0200 Subject: [PATCH 231/290] Add an abstract method to get the property id. --- .../staticobject/test/StaticObjectTest.java | 16 +++++++++++++++- .../staticobject/DefaultStaticProperty.java | 10 +++++----- .../espresso/staticobject/ShapeGenerator.java | 2 +- .../espresso/staticobject/StaticProperty.java | 2 ++ .../truffle/espresso/impl/LinkedField.java | 14 +++++++++----- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StaticObjectTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StaticObjectTest.java index a4c1e95284af..919f4cd4991e 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StaticObjectTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/StaticObjectTest.java @@ -22,8 +22,12 @@ */ package com.oracle.truffle.espresso.staticobject.test; +import com.oracle.truffle.espresso.staticobject.DefaultStaticProperty; import com.oracle.truffle.espresso.staticobject.StaticProperty; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + class StaticObjectTest { static final boolean ARRAY_BASED = !Boolean.getBoolean("com.oracle.truffle.espresso.staticobject.FieldBasedStorage"); @@ -31,6 +35,16 @@ String guessGeneratedFieldName(StaticProperty property) { assert !ARRAY_BASED; // The format of generated field names with the field-based storage might change at any // time. Do not depend on it! - return property + "@" + System.identityHashCode(property); + if (property instanceof DefaultStaticProperty) { + return ((DefaultStaticProperty) property).getId(); + } else { + try { + Method getId = StaticProperty.class.getDeclaredMethod("getId"); + getId.setAccessible(true); + return (String) getId.invoke(property); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java index c44a74519a20..cc19d77b99ff 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/DefaultStaticProperty.java @@ -23,15 +23,15 @@ package com.oracle.truffle.espresso.staticobject; public final class DefaultStaticProperty extends StaticProperty { - private final String name; + private final String id; - public DefaultStaticProperty(String name, StaticPropertyKind kind, boolean isFinal) { + public DefaultStaticProperty(String id, StaticPropertyKind kind, boolean isFinal) { super(kind, isFinal); - this.name = name; + this.id = id; } @Override - public String toString() { - return name; + public String getId() { + return id; } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java index 9c8ded8c145b..ecc846c7f128 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/ShapeGenerator.java @@ -89,7 +89,7 @@ static String generateFactoryName(Class generatedStorageClass) { } static String generateFieldName(StaticProperty property) { - return property.toString() + "@" + System.identityHashCode(property); + return property.getId(); } static void addStorageFields(ClassVisitor cv, Collection staticProperties) { diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 92a8108039fc..282222754638 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -43,6 +43,8 @@ protected StaticProperty(StaticPropertyKind kind, boolean isFinal) { this.flags = (byte) (isFinal ? IS_FINAL | internalKind : internalKind); } + protected abstract String getId(); + public final boolean isFinal() { return (flags & IS_FINAL) == IS_FINAL; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java index 9b5faabe7672..7dc2e24dcc1a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedField.java @@ -43,6 +43,15 @@ public static LinkedField createHidden(Symbol name, int slot) { return new LinkedField(new ParserField(ParserField.HIDDEN, name, Type.java_lang_Object, null), slot); } + /** + * This method is required by the Static Object Model. In Espresso we should rather call + * `getName()` and use Symbols. + */ + @Override + protected String getId() { + return getName().toString(); + } + public Symbol getName() { return parserField.getName(); } @@ -79,11 +88,6 @@ public boolean isHidden() { return parserField.isHidden(); } - @Override - public String toString() { - return getName().toString(); - } - ParserField getParserField() { return parserField; } From ecece8fc18cc7bd2c5fea03b2226aa6c60b90e7e Mon Sep 17 00:00:00 2001 From: Kristian Thomassen Date: Tue, 20 Apr 2021 20:05:47 +1000 Subject: [PATCH 232/290] Improve AbsNode canonicalization Add an extra canonicalization to AbsNode with NegateNode inputs, and also add unit tests. Also fix an incorrect return value in its create factory method. --- .../core/test/AbsCanonicalizationTest.java | 75 +++++++++++++++++++ .../graalvm/compiler/nodes/calc/AbsNode.java | 15 +++- 2 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/AbsCanonicalizationTest.java diff --git a/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/AbsCanonicalizationTest.java b/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/AbsCanonicalizationTest.java new file mode 100644 index 000000000000..f68a53416a2e --- /dev/null +++ b/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/AbsCanonicalizationTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 org.graalvm.compiler.core.test; + +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.calc.AbsNode; +import org.graalvm.compiler.nodes.calc.NegateNode; +import org.junit.Assert; +import org.junit.Test; + +public class AbsCanonicalizationTest extends GraalCompilerTest { + public static double absReference(double x) { + return Math.abs(x); + } + + public static double absAbs(double x) { + return Math.abs(Math.abs(x)); + } + + public static double absNegate(double x) { + return Math.abs(-x); + } + + @Test + public void testAbsNegate() { + StructuredGraph graph = parseEager("absNegate", StructuredGraph.AllowAssumptions.YES); + createInliningPhase().apply(graph, getDefaultHighTierContext()); + createCanonicalizerPhase().apply(graph, getProviders()); + + StructuredGraph referenceGraph = parseEager("absReference", StructuredGraph.AllowAssumptions.YES); + assertEquals(referenceGraph, graph); + Assert.assertEquals(0, graph.getNodes().filter(NegateNode.class).count()); + + testAgainstExpected(graph.method(), new Result(absNegate(-Double.MAX_VALUE), null), (Object) null, Double.MAX_VALUE); + testAgainstExpected(graph.method(), new Result(absNegate(0d), null), (Object) null, 0d); + testAgainstExpected(graph.method(), new Result(absNegate(Double.MAX_VALUE), null), (Object) null, Double.MAX_VALUE); + } + + @Test + public void testAbsAbs() { + StructuredGraph graph = parseEager("absAbs", StructuredGraph.AllowAssumptions.YES); + createInliningPhase().apply(graph, getDefaultHighTierContext()); + createCanonicalizerPhase().apply(graph, getProviders()); + + StructuredGraph referenceGraph = parseEager("absReference", StructuredGraph.AllowAssumptions.YES); + assertEquals(referenceGraph, graph); + Assert.assertEquals(1, graph.getNodes().filter(AbsNode.class).count()); + + testAgainstExpected(graph.method(), new Result(absAbs(-Double.MAX_VALUE), null), (Object) null, Double.MAX_VALUE); + testAgainstExpected(graph.method(), new Result(absAbs(0d), null), (Object) null, 0d); + testAgainstExpected(graph.method(), new Result(absAbs(Double.MAX_VALUE), null), (Object) null, Double.MAX_VALUE); + } +} diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java index 5eb674e7a99f..abffde43c914 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java @@ -56,7 +56,7 @@ public static ValueNode create(ValueNode value, NodeView view) { if (synonym != null) { return synonym; } - return new NegateNode(value); + return new AbsNode(value); } protected static ValueNode findSynonym(ValueNode forValue, NodeView view) { @@ -65,6 +65,14 @@ protected static ValueNode findSynonym(ValueNode forValue, NodeView view) { if (synonym != null) { return synonym; } + if (forValue instanceof AbsNode) { + return forValue; + } + // abs(-x) => abs(x) + if (forValue instanceof NegateNode) { + NegateNode negate = (NegateNode) forValue; + return AbsNode.create(negate.getValue(), view); + } return null; } @@ -79,8 +87,9 @@ public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { if (ret != this) { return ret; } - if (forValue instanceof AbsNode) { - return forValue; + ValueNode synonym = findSynonym(forValue, NodeView.from(tool)); + if (synonym != null) { + return synonym; } return this; } From e050c6079c79b257245a6d0d81b4a17b68f961e2 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 12 May 2021 15:14:41 +0200 Subject: [PATCH 233/290] Fix checks on property registration and the tests. --- .../test/BuilderPropertyTest.java | 19 +++++++++++++++---- .../staticobject/test/InheritanceTest.java | 9 +++------ .../espresso/staticobject/StaticProperty.java | 6 +++--- .../espresso/staticobject/StaticShape.java | 7 +++++-- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java index 55449d62e8d2..e328793ce823 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -40,8 +40,14 @@ public void sameBuilderSameProperty() { StaticShape.Builder builder = StaticShape.newBuilder(); StaticProperty property = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); builder.property(property); - builder.property(property); - builder.build(); + try { + // You cannot add the same property twice + builder.property(property); + Assert.fail(); + } catch (IllegalArgumentException e) { + // You cannot add the same property twice + Assert.assertEquals("This builder already contains a property named 'property'", e.getMessage()); + } } @Test @@ -50,8 +56,13 @@ public void sameBuilderSameName() throws IllegalArgumentException { StaticProperty p1 = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); StaticProperty p2 = new DefaultStaticProperty("property", StaticPropertyKind.Int, false); builder.property(p1); - builder.property(p2); - builder.build(); + try { + // You cannot add two properties with the same name + builder.property(p2); + Assert.fail(); + } catch (IllegalArgumentException e) { + Assert.assertEquals("This builder already contains a property named 'property'", e.getMessage()); + } } @Test diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java index 6d01150349e3..fe61e8fa0f2b 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/InheritanceTest.java @@ -59,12 +59,10 @@ public void baseClassInheritance() throws NoSuchFieldException { Assert.assertEquals(24, property.getInt(object)); Assume.assumeFalse(ARRAY_BASED); - // `CustomStaticObject.field1` is still visible - Assert.assertEquals(byte.class, object.getClass().getField("field1").getType()); + // `CustomStaticObject.field1` is shadowed + Assert.assertEquals(int.class, object.getClass().getField("field1").getType()); // `CustomStaticObject.field2` is visible Assert.assertEquals(boolean.class, object.getClass().getField("field2").getType()); - // The generated field is accessible - Assert.assertEquals(int.class, object.getClass().getField(guessGeneratedFieldName(property)).getType()); } @Test @@ -98,7 +96,6 @@ public void baseShapeInheritance() throws NoSuchFieldException, IllegalAccessExc Assert.assertEquals(3, s2p1.getInt(object)); Assume.assumeFalse(ARRAY_BASED); - Assert.assertEquals(1, object.getClass().getField(guessGeneratedFieldName(s1p1)).getInt(object)); - Assert.assertEquals(3, object.getClass().getField(guessGeneratedFieldName(s2p1)).getInt(object)); + Assert.assertEquals(3, object.getClass().getField("field1").getInt(object)); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java index 282222754638..cac1c8051fef 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticProperty.java @@ -59,7 +59,7 @@ final byte getInternalKind() { final void initOffset(int o) { if (this.offset != 0) { - throw new RuntimeException("Attempt to reinitialize the offset of static property '" + this + "' of kind '" + StaticPropertyKind.valueOf(getInternalKind()).name() + "'.\n" + + throw new RuntimeException("Attempt to reinitialize the offset of static property '" + getId() + "' of kind '" + StaticPropertyKind.valueOf(getInternalKind()).name() + "'.\n" + "Was it added to more than one builder or multiple times to the same builder?"); } this.offset = o; @@ -67,7 +67,7 @@ final void initOffset(int o) { final void initShape(StaticShape s) { if (this.shape != null) { - throw new RuntimeException("Attempt to reinitialize the shape of static property '" + this + "' of kind '" + StaticPropertyKind.valueOf(getInternalKind()).name() + "'.\n" + + throw new RuntimeException("Attempt to reinitialize the shape of static property '" + getId() + "' of kind '" + StaticPropertyKind.valueOf(getInternalKind()).name() + "'.\n" + "Was it added to more than one builder or multiple times to the same builder?"); } this.shape = s; @@ -78,7 +78,7 @@ private void checkKind(StaticPropertyKind kind) { if (internalKind != getInternalKind(kind)) { CompilerDirectives.transferToInterpreterAndInvalidate(); String kindName = StaticPropertyKind.valueOf(internalKind).name(); - throw new RuntimeException("Static property '" + this + "' of kind '" + kindName + "' cannot be accessed as '" + kind.name() + "'"); + throw new RuntimeException("Static property '" + getId() + "' of kind '" + kindName + "' cannot be accessed as '" + kind.name() + "'"); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 48e494548067..02717dc60e12 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -90,7 +90,7 @@ private static Unsafe getUnsafe() { } public static final class Builder { - private final HashMap staticProperties = new LinkedHashMap<>(); + private final HashMap staticProperties = new LinkedHashMap<>(); Builder() { } @@ -98,7 +98,10 @@ public static final class Builder { public Builder property(StaticProperty property) { CompilerAsserts.neverPartOfCompilation(); Objects.requireNonNull(property); - staticProperties.put(property, property); + if (staticProperties.containsKey(property.getId())) { + throw new IllegalArgumentException("This builder already contains a property named '" + property.getId() + "'"); + } + staticProperties.put(property.getId(), property); return this; } From fc8418839a02c1b2ed00e01073bf8e8764401e30 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Wed, 12 May 2021 15:39:53 +0200 Subject: [PATCH 234/290] lookup instrumentable node when getting scope in call frame --- .../oracle/truffle/espresso/jdwp/api/CallFrame.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 d1824060d032..9ef8efd0feb2 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 @@ -26,6 +26,7 @@ import com.oracle.truffle.api.debug.DebugStackFrame; import com.oracle.truffle.api.debug.DebugValue; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.instrumentation.InstrumentableNode; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.NodeLibrary; @@ -114,7 +115,7 @@ public Object getThisValue() { try { return theScope != null ? INTEROP.readMember(theScope, "this") : null; } catch (UnsupportedMessageException | UnknownIdentifierException e) { - JDWP.LOGGER.warning(() -> "Unable to read 'this' value from method: " + getMethod()); + JDWP.LOGGER.warning(() -> "Unable to read 'this' value from method: " + getMethod() + " with currentNode: " + currentNode.getClass()); return INVALID_VALUE; } } @@ -144,10 +145,11 @@ private Object getScope() { if (scope != null) { return scope; } - // check if current node has scope - if (NodeLibrary.getUncached().hasScope(currentNode, frame)) { + // look for instrumentable node that should have scope + Node node = InstrumentableNode.findInstrumentableParent(currentNode); + if (NodeLibrary.getUncached().hasScope(node, frame)) { try { - scope = NodeLibrary.getUncached().getScope(currentNode, frame, true); + scope = NodeLibrary.getUncached().getScope(node, frame, true); } catch (UnsupportedMessageException e) { JDWP.LOGGER.warning(() -> "Unable to get scope for " + currentNode.getClass()); } From 2ee9188f9de79807222d2ad7557324a562670001 Mon Sep 17 00:00:00 2001 From: Danilo Ansaloni Date: Wed, 12 May 2021 16:11:06 +0200 Subject: [PATCH 235/290] Improve checks on property id. --- .../test/BuilderPropertyTest.java | 4 +-- .../espresso/staticobject/StaticShape.java | 25 ++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java index e328793ce823..6337f1362193 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject.test/src/com/oracle/truffle/espresso/staticobject/test/BuilderPropertyTest.java @@ -46,7 +46,7 @@ public void sameBuilderSameProperty() { Assert.fail(); } catch (IllegalArgumentException e) { // You cannot add the same property twice - Assert.assertEquals("This builder already contains a property named 'property'", e.getMessage()); + Assert.assertEquals("This builder already contains a property with id 'property'", e.getMessage()); } } @@ -61,7 +61,7 @@ public void sameBuilderSameName() throws IllegalArgumentException { builder.property(p2); Assert.fail(); } catch (IllegalArgumentException e) { - Assert.assertEquals("This builder already contains a property named 'property'", e.getMessage()); + Assert.assertEquals("This builder already contains a property with id 'property'", e.getMessage()); } } diff --git a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java index 02717dc60e12..57560468a08a 100644 --- a/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java +++ b/espresso/src/com.oracle.truffle.espresso.staticobject/src/com/oracle/truffle/espresso/staticobject/StaticShape.java @@ -90,6 +90,7 @@ private static Unsafe getUnsafe() { } public static final class Builder { + private static final char[] FORBIDDEN_CHARS = new char[]{'.', ';', '[', '/'}; private final HashMap staticProperties = new LinkedHashMap<>(); Builder() { @@ -97,10 +98,7 @@ public static final class Builder { public Builder property(StaticProperty property) { CompilerAsserts.neverPartOfCompilation(); - Objects.requireNonNull(property); - if (staticProperties.containsKey(property.getId())) { - throw new IllegalArgumentException("This builder already contains a property named '" + property.getId() + "'"); - } + validatePropertyId(property.getId()); staticProperties.put(property.getId(), property); return this; } @@ -119,7 +117,7 @@ public StaticShape build(StaticShape parentShape) { } public StaticShape build(Class superClass, Class factoryInterface) { - validate(factoryInterface, superClass); + validateClasses(factoryInterface, superClass); ShapeGenerator sg = ShapeGenerator.getShapeGenerator(superClass, factoryInterface); return build(sg, null); } @@ -133,7 +131,22 @@ private StaticShape build(ShapeGenerator sg, StaticShape parentShap return shape; } - private static void validate(Class storageFactoryInterface, Class storageSuperClass) { + private void validatePropertyId(String id) { + Objects.requireNonNull(id); + if (id.length() == 0) { + throw new IllegalArgumentException("The property id cannot be an empty string"); + } + for (char forbidden : FORBIDDEN_CHARS) { + if (id.indexOf(forbidden) != -1) { + throw new IllegalArgumentException("Property id '" + id + "' contains a forbidden char: '" + forbidden + "'"); + } + } + if (staticProperties.containsKey(id)) { + throw new IllegalArgumentException("This builder already contains a property with id '" + id + "'"); + } + } + + private static void validateClasses(Class storageFactoryInterface, Class storageSuperClass) { CompilerAsserts.neverPartOfCompilation(); if (!storageFactoryInterface.isInterface()) { throw new RuntimeException(storageFactoryInterface.getName() + " must be an interface."); From 7cbb4ce6c34b86809a37197bd12f3a52a9f56be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C3=B6=20Barany?= Date: Wed, 12 May 2021 17:01:55 +0200 Subject: [PATCH 236/290] Add GraphEffectList.ensureFloatingAdded --- .../virtual/phases/ea/GraphEffectList.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java b/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java index 841349e43671..f176f98c3950 100644 --- a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java +++ b/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java @@ -42,6 +42,7 @@ import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.WithExceptionNode; +import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.debug.DynamicCounterNode; import org.graalvm.compiler.nodes.debug.WeakCounterNode; import org.graalvm.compiler.nodes.memory.MemoryKill; @@ -82,7 +83,8 @@ public void addWeakCounterCounterBefore(String group, String name, int increment /** * Adds the given fixed node to the graph's control flow, before position (so that the original - * predecessor of position will then be node's predecessor). + * predecessor of position will then be node's predecessor). The node must not yet be part of + * the graph. * * @param node The fixed node to be added to the graph. * @param position The fixed node before which the node should be added. @@ -94,6 +96,13 @@ public void addFixedNodeBefore(FixedWithNextNode node, FixedNode position) { }); } + /** + * Add {@code node} to the graph if it is not yet part of the graph. {@code node} must be a + * {@link FixedNode}. If it is not yet part of the graph, it is added before {@code position}. + * + * @param node The fixed node to be added to the graph if not yet present. + * @param position The fixed node before which the node should be added if not yet present. + */ public void ensureAdded(ValueNode node, FixedNode position) { add("ensure added", graph -> { assert position.isAlive(); @@ -116,16 +125,33 @@ public int getVirtualizationDelta() { } /** - * Add the given floating node to the graph. + * Add the given floating node to the graph. The node must not yet be part of the graph. * * @param node The floating node to be added. */ public void addFloatingNode(ValueNode node, @SuppressWarnings("unused") String cause) { add("add floating node", graph -> { + assert !node.isAlive() && !node.isDeleted(); graph.addWithoutUniqueWithInputs(node); }); } + /** + * Add {@code node} to the graph if it is not yet part of the graph. {@code node} must be a + * {@link FloatingNode}. + * + * @param node The floating node to be added to the graph if not yet present. + */ + public void ensureFloatingAdded(ValueNode node) { + add("ensure floating added", graph -> { + assert node instanceof FloatingNode; + assert !node.isDeleted(); + if (!node.isAlive()) { + graph.addWithoutUniqueWithInputs(node); + } + }); + } + /** * Sets the phi node's input at the given index to the given value, adding new phi inputs as * needed. From 3109623e9d940fd36cf38b5a71951faf52c61f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C3=B6=20Barany?= Date: Wed, 12 May 2021 17:25:30 +0200 Subject: [PATCH 237/290] Fix add of already added node in read elimination --- .../compiler/virtual/phases/ea/ReadEliminationClosure.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java b/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java index e6fbff1279bd..d0e6d5051842 100644 --- a/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java +++ b/compiler/src/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java @@ -181,7 +181,7 @@ protected boolean processNode(Node node, ReadEliminationBlockState state, GraphE // perform boolean coercion LogicNode cmp = IntegerEqualsNode.create(cachedValue, ConstantNode.forInt(0), NodeView.DEFAULT); ValueNode boolValue = ConditionalNode.create(cmp, ConstantNode.forBoolean(false), ConstantNode.forBoolean(true), NodeView.DEFAULT); - effects.addFloatingNode(boolValue, "boolean coercion"); + effects.ensureFloatingAdded(boolValue); cachedValue = boolValue; } effects.replaceAtUsages(load, cachedValue, load); From 1ea3e7c86f64ac760f0be9ee147d9132ca2500d1 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 12 May 2021 17:37:12 +0200 Subject: [PATCH 238/290] Add LoopConditionProfile#create() for convenience * So it can be used like `@Cached LoopConditionProfile loopProfile`. --- truffle/CHANGELOG.md | 1 + .../truffle/api/dsl/test/CachedTest.java | 42 ++++++++++++++++--- .../snapshot.sigtest | 1 + .../api/profiles/LoopConditionProfile.java | 12 ++++++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index aa795ff2f6bd..a47f78ea9c9a 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -14,6 +14,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan * Deprecated and added methods to support expected arity ranges in `ArityException` instances. Note that the replacement methods now include more strict validations. * `DebugValue` methods `hashCode()` and `equals()` provide result of the interop `identityHashCode` and `isIdentical` calls on the corresponding guest objects, respectively. * Enabled by default the traversing compilation queue with dynamic thresholds, see `PolyglotCompilerOptions#TraversingCompilationQueue`, `PolyglotCompilerOptions#DynamicCompilationThresholds`, `PolyglotCompilerOptions#DynamicCompilerThresholdsMinScale`, `PolyglotCompilerOptions#DynamicCompilerThresholdsMinNormalLoad` and `PolyglotCompilerOptions#DynamicCompilerThresholdsMaxNormalLoad`. +* Added `LoopConditionProfile#create()` as an alias of `createCountingProfile()` so it can be used like `@Cached LoopConditionProfile loopProfile`. ## Version 21.1.0 * Added methods into `Instrumenter` that create bindings to be attached later on. Added `EventBinding.attach()` method. diff --git a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java index 49de4a2bdfb3..caba5c7b36e1 100644 --- a/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java +++ b/truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java @@ -44,11 +44,16 @@ import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; import static com.oracle.truffle.api.dsl.test.TestHelper.createNode; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.lang.reflect.Field; +import com.oracle.truffle.api.dsl.test.CachedTestFactory.ProfileNodeGen; +import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.api.profiles.LoopConditionProfile; import org.junit.Assert; import org.junit.Test; @@ -846,7 +851,7 @@ public void testChildrenNoAdoption1() { Node child = new ValueNode(); root.execute(child); root.adoptChildren(); - Assert.assertFalse(hasParent(root, child)); + assertFalse(hasParent(root, child)); } @Test @@ -854,7 +859,7 @@ public void testChildrenNoAdoption2() { ChildrenNoAdoption2 root = createNode(ChildrenNoAdoption2Factory.getInstance(), false); Node[] children = new Node[]{new ValueNode()}; root.execute(children); - Assert.assertFalse(hasParent(root, children[0])); + assertFalse(hasParent(root, children[0])); } @Test @@ -862,7 +867,7 @@ public void testChildrenNoAdoption3() { ChildrenNoAdoption3 root = createNode(ChildrenNoAdoption3Factory.getInstance(), false); Node child = new ValueNode(); root.execute(child); - Assert.assertFalse(hasParent(root, child)); + assertFalse(hasParent(root, child)); } @Test @@ -870,7 +875,7 @@ public void testChildrenNoAdoption4() { ChildrenNoAdoption4 root = createNode(ChildrenNoAdoption4Factory.getInstance(), false); Node child = new ValueNode(); root.execute(child); - Assert.assertFalse(hasParent(root, child)); + assertFalse(hasParent(root, child)); } @Test @@ -878,7 +883,7 @@ public void testChildrenNoAdoption5() { ChildrenNoAdoption5 root = createNode(ChildrenNoAdoption5Factory.getInstance(), false); Node child = new ValueNode(); root.execute(child); - Assert.assertFalse(hasParent(root, child)); + assertFalse(hasParent(root, child)); } @Test @@ -886,7 +891,7 @@ public void testChildrenNoAdoption6() { ChildrenNoAdoption6 root = createNode(ChildrenNoAdoption6Factory.getInstance(), false); ConstantValueNode child = new ConstantValueNode(); root.execute(child); - Assert.assertFalse(hasParent(root, child)); + assertFalse(hasParent(root, child)); } @GenerateUncached @@ -910,6 +915,31 @@ public void testNullLiteral() { assertNull(NullLiteralNodeGen.getUncached().execute("")); } + @GenerateUncached + abstract static class ProfileNode extends Node { + abstract Object execute(Object value); + + @Specialization + static ConditionProfile do1(String value, // + @Cached ConditionProfile conditionProfile) { + return conditionProfile; + } + + @Specialization + static LoopConditionProfile do1(int value, // + @Cached LoopConditionProfile loopProfile) { + return loopProfile; + } + } + + @Test + public void testConditionProfileLoopConditionProfile() { + final Object conditionProfile = ProfileNodeGen.getUncached().execute(""); + assertTrue(conditionProfile instanceof ConditionProfile); + assertFalse(conditionProfile instanceof LoopConditionProfile); + assertTrue(ProfileNodeGen.getUncached().execute(0) instanceof LoopConditionProfile); + } + private static boolean hasParent(Node parent, Node node) { Node current = node != null ? node.getParent() : null; while (current != null) { diff --git a/truffle/src/com.oracle.truffle.api.profiles/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.profiles/snapshot.sigtest index 25dd075a471a..2d46a82d4ee2 100644 --- a/truffle/src/com.oracle.truffle.api.profiles/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api.profiles/snapshot.sigtest @@ -62,6 +62,7 @@ CLSS public abstract com.oracle.truffle.api.profiles.LoopConditionProfile meth public abstract boolean inject(boolean) meth public abstract boolean profile(boolean) meth public abstract void profileCounted(long) +meth public static com.oracle.truffle.api.profiles.LoopConditionProfile create() meth public static com.oracle.truffle.api.profiles.LoopConditionProfile createCountingProfile() meth public static com.oracle.truffle.api.profiles.LoopConditionProfile getUncached() supr com.oracle.truffle.api.profiles.ConditionProfile diff --git a/truffle/src/com.oracle.truffle.api.profiles/src/com/oracle/truffle/api/profiles/LoopConditionProfile.java b/truffle/src/com.oracle.truffle.api.profiles/src/com/oracle/truffle/api/profiles/LoopConditionProfile.java index 9c66458020b1..1cb6bc5f87bd 100644 --- a/truffle/src/com.oracle.truffle.api.profiles/src/com/oracle/truffle/api/profiles/LoopConditionProfile.java +++ b/truffle/src/com.oracle.truffle.api.profiles/src/com/oracle/truffle/api/profiles/LoopConditionProfile.java @@ -145,6 +145,18 @@ public static LoopConditionProfile createCountingProfile() { } } + /** + * Creates a {@link LoopConditionProfile} using {@link #createCountingProfile()}. This is a + * convenience method so it can be used as {@code @Cached LoopConditionProfile loopProfile} + * instead of the much longer + * {@code @Cached("createCountingProfile()") LoopConditionProfile loopProfile}. + * + * @since 21.2 + */ + public static LoopConditionProfile create() { + return createCountingProfile(); + } + /** * Returns the uncached version of the profile. The uncached version of a profile does nothing. * From 9a44294218d8cb7c5746bbe5db5d4e386fd4fdff Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Wed, 12 May 2021 17:55:29 +0200 Subject: [PATCH 239/290] [GR-31285] Workaround missing JFR events using trace compilation details in the polybench. --- vm/mx.vm/suite.py | 17 -- .../src/org/graalvm/polybench/JFRSupport.java | 96 ---------- .../org/graalvm/polybench/proxy-config.json | 3 - .../native-image.properties | 2 - .../polybench/CompilationTimeMetric.java | 167 +++++++++++------- .../src/org/graalvm/polybench/JFRSupport.java | 57 ------ .../src/org/graalvm/polybench/Metric.java | 8 + .../graalvm/polybench/PolyBenchLauncher.java | 6 +- .../org/graalvm/polybench/proxy-config.json | 1 - 9 files changed, 116 insertions(+), 241 deletions(-) delete mode 100644 vm/src/org.graalvm.polybench.jdk11/src/org/graalvm/polybench/JFRSupport.java delete mode 100644 vm/src/org.graalvm.polybench.jdk11/src/org/graalvm/polybench/proxy-config.json delete mode 100644 vm/src/org.graalvm.polybench/src/META-INF/native-image/org.graalvm.polybench/native-image.properties delete mode 100644 vm/src/org.graalvm.polybench/src/org/graalvm/polybench/JFRSupport.java delete mode 100644 vm/src/org.graalvm.polybench/src/org/graalvm/polybench/proxy-config.json diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index 572d48f83d01..fb75fdd0f348 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -118,23 +118,6 @@ "sdk:LAUNCHER_COMMON", ], }, - "org.graalvm.polybench.jdk11" : { - "subDir" : "src", - "sourceDirs" : ["src"], - "dependencies" : [ - "org.graalvm.polybench", - ], - "requires" : [ - "java.logging", - "jdk.jfr", - ], - "javaCompliance" : "11+", - "license" : "GPLv2-CPE", - "checkstyle": "org.graalvm.component.installer", - "checkPackagePrefix" : "false", - "overlayTarget" : "org.graalvm.polybench", - "multiReleaseJarVersion" : "11", - }, }, "distributions": { diff --git a/vm/src/org.graalvm.polybench.jdk11/src/org/graalvm/polybench/JFRSupport.java b/vm/src/org.graalvm.polybench.jdk11/src/org/graalvm/polybench/JFRSupport.java deleted file mode 100644 index 820f83d8d137..000000000000 --- a/vm/src/org.graalvm.polybench.jdk11/src/org/graalvm/polybench/JFRSupport.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2020, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 org.graalvm.polybench; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.Duration; -import java.util.function.Function; -import java.util.stream.Collectors; -import jdk.jfr.FlightRecorder; -import jdk.jfr.Recording; -import jdk.jfr.consumer.RecordedEvent; -import jdk.jfr.consumer.RecordingFile; - -final class JFRSupport { - - private JFRSupport() { - } - - static boolean isAvailable() { - try { - return FlightRecorder.isAvailable(); - } catch (LinkageError e) { - // Thrown on the JDK-11 CE native-image without JFR support. - return false; - } - } - - static Object startRecording(String enabledEvent) { - Recording recording = new Recording(); - recording.enable(enabledEvent); - recording.setDumpOnExit(false); - recording.start(); - return recording; - } - - static Object snapshotRecording(Object recording) { - return ((Recording) recording).copy(true); - } - - static void disposeRecording(Object recording, boolean stop) { - if (recording != null) { - Recording r = (Recording) recording; - if (stop) { - r.stop(); - } - r.close(); - } - } - - static long computeCumulativeTime(Object recording, String eventName, String fieldName) throws IOException { - Path file = Files.createTempFile("recording", ".jfr"); - try { - // Copy a JFR events snapshot into a temp file. - ((Recording) recording).dump(file); - // Calculate a cumulative Truffle compilation time from all events in the snapshot. - return processRecordings(file, eventName, fieldName); - } finally { - Files.delete(file); - } - } - - private static long processRecordings(Path jfrFile, String eventName, String fieldName) throws IOException { - Function mapper = fieldName == null ? (event) -> event.getDuration() : (event) -> Duration.ofMillis(event.getLong(fieldName)); - return RecordingFile.readAllEvents(jfrFile).stream() - .filter((event) -> { - return eventName.equals(event.getEventType().getName()); - }) - .map(mapper) - .collect(Collectors.reducing(Duration.ofNanos(0), (a, b) -> a.plus(b))) - .toMillis(); - } -} diff --git a/vm/src/org.graalvm.polybench.jdk11/src/org/graalvm/polybench/proxy-config.json b/vm/src/org.graalvm.polybench.jdk11/src/org/graalvm/polybench/proxy-config.json deleted file mode 100644 index 8595090431ae..000000000000 --- a/vm/src/org.graalvm.polybench.jdk11/src/org/graalvm/polybench/proxy-config.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - ["jdk.jfr.Unsigned"] -] \ No newline at end of file diff --git a/vm/src/org.graalvm.polybench/src/META-INF/native-image/org.graalvm.polybench/native-image.properties b/vm/src/org.graalvm.polybench/src/META-INF/native-image/org.graalvm.polybench/native-image.properties deleted file mode 100644 index bbebbc26e064..000000000000 --- a/vm/src/org.graalvm.polybench/src/META-INF/native-image/org.graalvm.polybench/native-image.properties +++ /dev/null @@ -1,2 +0,0 @@ -Args = -H:+AllowVMInspection \ - -H:DynamicProxyConfigurationResources=org/graalvm/polybench/proxy-config.json diff --git a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/CompilationTimeMetric.java b/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/CompilationTimeMetric.java index bcf7daf922a4..0714389d9e03 100644 --- a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/CompilationTimeMetric.java +++ b/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/CompilationTimeMetric.java @@ -24,45 +24,24 @@ */ package org.graalvm.polybench; -import java.io.IOException; -import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.concurrent.atomic.DoubleAdder; +import java.util.logging.Handler; +import java.util.logging.LogRecord; final class CompilationTimeMetric implements Metric { enum MetricType { - COMPILATION(null), - PARTIAL_EVALUATION("peTime"); - - private final String fieldName; - - MetricType(String fieldName) { - this.fieldName = fieldName; - } - - String getFieldName() { - return fieldName; - } + COMPILATION, + PARTIAL_EVALUATION; } - private static final Logger LOG = Logger.getLogger(CompilationTimeMetric.class.getName()); - private static final String TRUFFLE_COMPILATION_EVENT = "org.graalvm.compiler.truffle.Compilation"; - - private final MetricType metricType; - /** - * {@code true} is the JFR is supported by the VM. We need to run also on the native-image CE - * which does not support JFR. In this case this metric returns {@code 0} in each report. - */ - private final boolean supported; - private Object recording; - private Object snapshot; + private final TraceCompilationHandler logHandler; CompilationTimeMetric(MetricType metricType) { - this.metricType = metricType; - this.supported = JFRSupport.isAvailable(); + this.logHandler = new TraceCompilationHandler(metricType); } @Override @@ -71,11 +50,23 @@ public void validateConfig(Config config, Map polyglotOptions) { throw new IllegalStateException("The Compile Time Metric cannot be used with a background compilation.\n" + "Remove the 'engine.BackgroundCompilation=true' option."); } + if (polyglotOptions.containsKey("engine.TraceCompilation") && !Boolean.parseBoolean(polyglotOptions.get("engine.TraceCompilation"))) { + throw new IllegalStateException("The Compile Time Metric cannot be used without TraceCompilation.\n" + + "Remove the 'engine.TraceCompilation=false' option."); + } } @Override public Map getEngineOptions(Config config) { - return Collections.singletonMap("engine.BackgroundCompilation", "false"); + Map options = new HashMap<>(); + options.put("engine.BackgroundCompilation", "false"); + options.put("engine.TraceCompilation", "true"); + return options; + } + + @Override + public Handler getLogHandler() { + return logHandler; } @Override @@ -90,59 +81,107 @@ public String name() { @Override public void beforeIteration(boolean warmup, int iteration, Config config) { - if (supported) { - if (recording == null) { - // First iteration, create a new Recording used for all iterations until reset. - recording = JFRSupport.startRecording(TRUFFLE_COMPILATION_EVENT); - } - JFRSupport.disposeRecording(snapshot, false); - snapshot = null; - } + logHandler.startIteration(); } @Override public void afterIteration(boolean warmup, int iteration, Config config) { - if (supported) { - if (recording == null) { - throw new IllegalStateException("Missing JFR recording."); - } - if (snapshot != null) { - throw new IllegalStateException("Existing JFR snapshot."); - } - snapshot = JFRSupport.snapshotRecording(recording); - } + logHandler.endIteration(); } @Override public void reset() { - if (supported) { - // Stop and dispose JFR recording. - JFRSupport.disposeRecording(recording, true); - recording = null; - JFRSupport.disposeRecording(snapshot, false); - snapshot = null; - } + logHandler.reset(); } @Override public Optional reportAfterIteration(Config config) { - return supported ? computeCumulativeTime() : Optional.of(0.0); + return logHandler.iterationTime(); } @Override public Optional reportAfterAll() { - return supported && recording != null ? computeCumulativeTime() : Optional.of(0.0); + return logHandler.allIterationsTime(); } - private Optional computeCumulativeTime() { - if (snapshot == null) { - throw new IllegalStateException("No snapshot."); + private static final class TraceCompilationHandler extends Handler { + + private final MetricType metricType; + private double allIterationsTime; + private final DoubleAdder iterationTime = new DoubleAdder(); + + TraceCompilationHandler(MetricType metricType) { + this.metricType = metricType; } - try { - return Optional.of(1.0 * JFRSupport.computeCumulativeTime(snapshot, TRUFFLE_COMPILATION_EVENT, metricType.getFieldName())); - } catch (IOException ioe) { - LOG.log(Level.SEVERE, "Cannot write recording.", ioe); - return Optional.empty(); + + @Override + public void publish(LogRecord record) { + if ("engine".equals(record.getLoggerName())) { + String message = record.getMessage(); + if (message.startsWith("opt done") || message.startsWith("opt failed")) { + double time = parseTime(message, metricType); + if (!Double.isNaN(time)) { + iterationTime.add(time); + } else { + throw new IllegalStateException("Failed to parse '" + message + "'"); + } + } + } + } + + private static double parseTime(String str, MetricType type) { + String timePattern = "|Time"; + int start = str.indexOf(timePattern); + if (start < 0) { + return Double.NaN; + } + start += timePattern.length(); + int openBraceIndex = str.indexOf('(', start); + if (openBraceIndex < 0) { + return Double.NaN; + } + switch (type) { + case COMPILATION: + return Double.parseDouble(str.substring(start, openBraceIndex).trim()); + case PARTIAL_EVALUATION: + start = openBraceIndex + 1; + int end = str.indexOf('+', start); + if (end < 0) { + return Double.NaN; + } + return Double.parseDouble(str.substring(start, end).trim()); + default: + throw new IllegalArgumentException(type.name()); + } + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } + + void startIteration() { + iterationTime.reset(); + } + + void endIteration() { + allIterationsTime += iterationTime.doubleValue(); + } + + void reset() { + iterationTime.reset(); + allIterationsTime = 0d; + } + + Optional iterationTime() { + return Optional.of(iterationTime.doubleValue()); + } + + Optional allIterationsTime() { + return Optional.of(allIterationsTime); } } } diff --git a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/JFRSupport.java b/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/JFRSupport.java deleted file mode 100644 index e11e501643d2..000000000000 --- a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/JFRSupport.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 org.graalvm.polybench; - -import java.io.IOException; - -final class JFRSupport { - - private JFRSupport() { - } - - static boolean isAvailable() { - return false; - } - - @SuppressWarnings("unused") - static Object startRecording(String enabledEvent) { - throw new UnsupportedOperationException(); - } - - @SuppressWarnings("unused") - static Object snapshotRecording(Object recording) { - throw new UnsupportedOperationException(); - } - - @SuppressWarnings("unused") - static void disposeRecording(Object recording, boolean stop) { - throw new UnsupportedOperationException(); - } - - @SuppressWarnings("unused") - static long computeCumulativeTime(Object recording, String eventName, String fieldName) throws IOException { - throw new UnsupportedOperationException(); - } -} diff --git a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/Metric.java b/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/Metric.java index 625542403c4f..161a630a2fd4 100644 --- a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/Metric.java +++ b/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/Metric.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.Map; import java.util.Optional; +import java.util.logging.Handler; @SuppressWarnings("unused") interface Metric { @@ -48,6 +49,13 @@ default Map getEngineOptions(Config config) { return Collections.emptyMap(); } + /** + * Allows Metric to forward engine logging into supplied logger. + */ + default Handler getLogHandler() { + return null; + } + default void beforeIteration(boolean warmup, int iteration, Config config) { } diff --git a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/PolyBenchLauncher.java b/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/PolyBenchLauncher.java index f286b620f95f..0faa05939672 100644 --- a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/PolyBenchLauncher.java +++ b/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/PolyBenchLauncher.java @@ -34,6 +34,7 @@ import java.util.NoSuchElementException; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.logging.Handler; import org.graalvm.launcher.AbstractLanguageLauncher; import org.graalvm.options.OptionCategory; @@ -206,7 +207,10 @@ private void runHarness(Context.Builder contextBuilder) { throw new AssertionError("Unknown execution-mode: " + config.mode); } contextBuilder.options(config.metric.getEngineOptions(config)); - + Handler handler = config.metric.getLogHandler(); + if (handler != null) { + contextBuilder.logHandler(handler); + } try (Context context = contextBuilder.build()) { log("::: Initializing :::"); diff --git a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/proxy-config.json b/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/proxy-config.json deleted file mode 100644 index 0637a088a01e..000000000000 --- a/vm/src/org.graalvm.polybench/src/org/graalvm/polybench/proxy-config.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file From 1d89b862ed6c4a083ad456f8f58ae8e9ea4eb677 Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Tue, 27 Apr 2021 16:24:11 +0200 Subject: [PATCH 240/290] Redo native-image deoptimization entrypoint logic. Changed deoptimization entrypoint strategy to identify deoptimization entrypoint points during the creation of bci blocks and to directly insert DeoptEntryNodes and DeoptProxyAnchorNode during parsing. --- .../compiler/java/BciBlockMapping.java | 169 +++-- .../graalvm/compiler/java/BytecodeParser.java | 100 +-- .../graalvm/compiler/java/LocalLiveness.java | 10 +- .../graphbuilderconf/GraphBuilderContext.java | 10 - .../StandardGraphBuilderPlugins.java | 2 - .../oracle/svm/core/deopt/Deoptimizer.java | 17 +- .../phases/HostedGraphBuilderPhase.java | 589 ++++++++++++------ .../phases/SharedGraphBuilderPhase.java | 2 +- 8 files changed, 554 insertions(+), 345 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java index ba778ead9a86..3bf134b9bb83 100644 --- a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java +++ b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BciBlockMapping.java @@ -276,8 +276,7 @@ * list). *

    * If a bytecode is covered by multiple exception handlers, a chain of exception dispatch blocks is - * created so that multiple exception handler types can be checked. The chains are re-used if - * multiple bytecodes are covered by the same exception handlers. + * created so that multiple exception handler types can be checked. *

    * Note that exception unwinds, i.e., bytecodes that can throw an exception but the exception is not * handled in this method, do not end a basic block. Not modeling the exception unwind block reduces @@ -300,7 +299,7 @@ * The algorithms and analysis in this class are conservative and do not use any assumptions or * profiling information. */ -public final class BciBlockMapping implements JavaMethodContext { +public class BciBlockMapping implements JavaMethodContext { public static class Options { @Option(help = "When enabled, some limited amount of duplication will be performed in order compile code containing irreducible loops.")// public static final OptionKey DuplicateIrreducibleLoops = new OptionKey<>(true); @@ -314,7 +313,7 @@ public static class BciBlock implements Cloneable { int id = UNASSIGNED_ID; final int startBci; - int endBci; // The bci of the last bytecode in the block + private int endBci; // The bci of the last bytecode in the block private boolean isExceptionEntry; private boolean isLoopHeader; int loopId; @@ -350,6 +349,11 @@ public JSRData copy() { this.successors = new ArrayList<>(); } + protected BciBlock(int startBci, int endBci) { + this(startBci); + this.endBci = endBci; + } + public boolean bciUnique() { return jsrData == null && !duplicate; } @@ -362,6 +366,10 @@ public int getEndBci() { return endBci; } + public void setEndBci(int bci) { + endBci = bci; + } + public long getLoops() { return loops; } @@ -453,6 +461,10 @@ public boolean isExceptionEntry() { return isExceptionEntry; } + public void setIsExceptionEntry() { + isExceptionEntry = true; + } + public BciBlock getSuccessor(int index) { return successors.get(index); } @@ -461,6 +473,10 @@ public int getLoopId() { return loopId; } + public boolean isDuplicate() { + return duplicate; + } + private JSRData getOrCreateJSRData() { if (jsrData == null) { jsrData = new JSRData(); @@ -567,8 +583,13 @@ public void clearSucccessors() { successors.clear(); } - public boolean isExceptionDispatch() { - return false; + /** + * A block is considered to be an instruction block if during parsing nodes the bytecodes + * within the range [startBci, endBci] are generated when processing this block by + * BytecodeProcess.processBlock. + */ + public boolean isInstructionBlock() { + return true; } public void getDebugProperties(Map properties) { @@ -594,9 +615,8 @@ public static class ExceptionDispatchBlock extends BciBlock { /** * Constructor for a normal dispatcher. */ - ExceptionDispatchBlock(ExceptionHandler handler, int deoptBci) { - super(handler.getHandlerBCI()); - this.endBci = startBci; + protected ExceptionDispatchBlock(ExceptionHandler handler, int deoptBci) { + super(handler.getHandlerBCI(), handler.getHandlerBCI()); this.deoptBci = deoptBci; this.handler = handler; } @@ -604,16 +624,25 @@ public static class ExceptionDispatchBlock extends BciBlock { /** * Constructor for the method unwind dispatcher. */ - ExceptionDispatchBlock(int deoptBci) { - super(deoptBci); - this.endBci = deoptBci; + protected ExceptionDispatchBlock(int deoptBci) { + super(deoptBci, deoptBci); this.deoptBci = deoptBci; this.handler = null; } @Override - public boolean isExceptionDispatch() { - return true; + public void setEndBci(int bci) { + throw GraalError.shouldNotReachHere(); + } + + @Override + public void setIsExceptionEntry() { + throw GraalError.shouldNotReachHere("Dispatch block cannot be exception entry."); + } + + @Override + public boolean isInstructionBlock() { + return false; } @Override @@ -689,14 +718,14 @@ public String toString() { public final Bytecode code; public boolean hasJsrBytecodes; - private final ExceptionHandler[] exceptionHandlers; + protected final ExceptionHandler[] exceptionHandlers; private BciBlock startBlock; private BciBlock[] loopHeaders; private static final int LOOP_HEADER_MAX_CAPACITY = Long.SIZE; private static final int LOOP_HEADER_INITIAL_CAPACITY = 4; - private int blocksNotYetAssignedId; + protected int blocksNotYetAssignedId; private final DebugContext debug; private int postJsrBlockCount; private int newDuplicateBlocks; @@ -705,7 +734,7 @@ public String toString() { /** * Creates a new BlockMap instance from {@code code}. */ - private BciBlockMapping(Bytecode code, DebugContext debug) { + protected BciBlockMapping(Bytecode code, DebugContext debug) { this.code = code; this.debug = debug; this.exceptionHandlers = code.getExceptionHandlers(); @@ -761,7 +790,7 @@ public void build(BytecodeStream stream, OptionValues options) { } } - private boolean verify() { + protected boolean verify() { for (BciBlock block : blocks) { assert blocks[block.getId()] == block; for (int i = 0; i < block.getSuccessorCount(); i++) { @@ -779,10 +808,31 @@ private void makeExceptionEntries(BciBlock[] blockMap) { // start basic blocks at all exception handler blocks and mark them as exception entries for (ExceptionHandler h : this.exceptionHandlers) { BciBlock xhandler = makeBlock(blockMap, h.getHandlerBCI()); - xhandler.isExceptionEntry = true; + xhandler.setIsExceptionEntry(); } } + /** + * Check whether this bci should be the start of a new block. + */ + protected boolean isStartOfNewBlock(BciBlock[] blockMap, BciBlock current, int bci) { + /* + * A new block must be created if either there is not a block currently being processed this + * bci can be appended to (current == null) or if this bci is has an explicit predecessor + * from another block (blockMap[bci] != null). + */ + return current == null || blockMap[bci] != null; + } + + /** + * Retrieve the instruction block corresponding to this bci. The criteria for being an + * instruction block is defined at BlockMap.isInstructionBlock. + */ + protected BciBlock getInstructionBlock(BciBlock[] blockMap, int bci) { + assert blockMap[bci].isInstructionBlock(); + return blockMap[bci]; + } + private void iterateOverBytecodes(BciBlock[] blockMap, BytecodeStream stream) { // iterate over the bytecodes top to bottom. // mark the entrypoints of basic blocks and build lists of successors for @@ -792,15 +842,16 @@ private void iterateOverBytecodes(BciBlock[] blockMap, BytecodeStream stream) { while (stream.currentBC() != Bytecodes.END) { int bci = stream.currentBCI(); - if (current == null || blockMap[bci] != null) { + if (isStartOfNewBlock(blockMap, current, bci)) { BciBlock b = makeBlock(blockMap, bci); if (current != null) { - addSuccessor(blockMap, current.endBci, b); + addSuccessor(blockMap, current.getEndBci(), b); } current = b; } blockMap[bci] = current; - current.endBci = bci; + current = getInstructionBlock(blockMap, bci); + current.setEndBci(bci); switch (stream.currentBC()) { case IRETURN: // fall through @@ -814,7 +865,7 @@ private void iterateOverBytecodes(BciBlock[] blockMap, BytecodeStream stream) { } case ATHROW: { current = null; - ExceptionDispatchBlock handler = handleExceptions(blockMap, bci); + ExceptionDispatchBlock handler = handleExceptions(blockMap, false, bci); if (handler != null) { addSuccessor(blockMap, bci, handler); } @@ -882,8 +933,8 @@ private void iterateOverBytecodes(BciBlock[] blockMap, BytecodeStream stream) { case INVOKEVIRTUAL: case INVOKEDYNAMIC: { current = null; - addSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI())); - ExceptionDispatchBlock handler = handleExceptions(blockMap, bci); + addInvokeNormalSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI())); + ExceptionDispatchBlock handler = handleExceptions(blockMap, true, bci); if (handler != null) { addSuccessor(blockMap, bci, handler); } @@ -930,7 +981,7 @@ private void iterateOverBytecodes(BciBlock[] blockMap, BytecodeStream stream) { * because the class initializer is allowed to throw an exception, which * requires proper exception handling. */ - ExceptionDispatchBlock handler = handleExceptions(blockMap, bci); + ExceptionDispatchBlock handler = handleExceptions(blockMap, false, bci); if (handler != null) { current = null; addSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI())); @@ -1085,29 +1136,41 @@ private void iterateOverBytecodes(BciBlock[] blockMap, BytecodeStream stream) { } } + /** + * A hook for subclasses to insert additional blocks around a newly created BciBlock. + */ + @SuppressWarnings("unused") + protected BciBlock processNewBciBlock(BciBlock[] blockMap, int bci, BciBlock newBlock) { + /* By default, no additional processing is needed. */ + return newBlock; + } + private BciBlock makeBlock(BciBlock[] blockMap, int startBci) { BciBlock oldBlock = blockMap[startBci]; if (oldBlock == null) { BciBlock newBlock = new BciBlock(startBci); blocksNotYetAssignedId++; blockMap[startBci] = newBlock; - return newBlock; + return processNewBciBlock(blockMap, startBci, newBlock); } else if (oldBlock.startBci != startBci) { - // Backward branch into the middle of an already processed block. - // Add the correct fall-through successor. + /* + * Backward branch into the middle of an already processed block. Split prior block and + * add the correct fall-through successor. + */ + assert oldBlock.isInstructionBlock(); BciBlock newBlock = new BciBlock(startBci); blocksNotYetAssignedId++; - newBlock.endBci = oldBlock.endBci; + newBlock.setEndBci(oldBlock.getEndBci()); for (BciBlock oldSuccessor : oldBlock.getSuccessors()) { newBlock.addSuccessor(oldSuccessor); } - oldBlock.endBci = startBci - 1; + oldBlock.setEndBci(startBci - 1); oldBlock.clearSucccessors(); oldBlock.addSuccessor(newBlock); - for (int i = startBci; i <= newBlock.endBci; i++) { + for (int i = startBci; i <= newBlock.getEndBci(); i++) { blockMap[i] = newBlock; } return newBlock; @@ -1129,14 +1192,21 @@ private void addSwitchSuccessors(BciBlock[] blockMap, int predBci, BytecodeSwitc } } - private static void addSuccessor(BciBlock[] blockMap, int predBci, BciBlock sux) { - BciBlock predecessor = blockMap[predBci]; - if (sux.isExceptionEntry) { + private void addSuccessor(BciBlock[] blockMap, int predBci, BciBlock sux) { + BciBlock predecessor = getInstructionBlock(blockMap, predBci); + if (sux.isExceptionEntry()) { throw new PermanentBailoutException("Exception handler can be reached by both normal and exceptional control flow"); } predecessor.addSuccessor(sux); } + /** + * Logic for adding an the "normal" invoke successor link. + */ + protected void addInvokeNormalSuccessor(BciBlock[] blockMap, int invokeBci, BciBlock sux) { + addSuccessor(blockMap, invokeBci, sux); + } + private final ArrayList jsrVisited = new ArrayList<>(); private void createJsrAlternatives(BciBlock[] blockMap, BciBlock block) { @@ -1204,7 +1274,17 @@ private static boolean shouldFollowEdge(BciBlock successor, JsrScope scope) { return true; } - private ExceptionDispatchBlock handleExceptions(BciBlock[] blockMap, int bci) { + /** + * A hook for subclasses to insert additional blocks around a newly created + * ExceptionDispatchBlock. + */ + @SuppressWarnings("unused") + protected ExceptionDispatchBlock processNewExceptionDispatchBlock(int bci, boolean isInvoke, ExceptionDispatchBlock handler) { + /* By default, no additional processing is needed. */ + return handler; + } + + private ExceptionDispatchBlock handleExceptions(BciBlock[] blockMap, boolean isInvoke, int bci) { ExceptionDispatchBlock lastHandler = null; int dispatchBlocks = 0; @@ -1230,7 +1310,7 @@ private ExceptionDispatchBlock handleExceptions(BciBlock[] blockMap, int bci) { } } blocksNotYetAssignedId += dispatchBlocks; - return lastHandler; + return processNewExceptionDispatchBlock(bci, isInvoke, lastHandler); } private void computeBlockOrder(BciBlock[] blockMap) { @@ -1307,11 +1387,11 @@ public static String toString(BciBlock[] blockMap, BciBlock[] loopHeadersMap) { if (b == null) { continue; } - sb.append("B").append(getId.applyAsInt(b)).append("[").append(b.startBci).append("..").append(b.endBci).append("]"); + sb.append("B").append(getId.applyAsInt(b)).append("[").append(b.startBci).append("..").append(b.getEndBci()).append("]"); if (b.isLoopHeader) { sb.append(" LoopHeader"); } - if (b.isExceptionEntry) { + if (b.isExceptionEntry()) { sb.append(" ExceptionEntry"); } if (b instanceof ExceptionDispatchBlock) { @@ -1365,7 +1445,7 @@ public BciBlock getLoopHeader(int index) { private void makeLoopHeader(BciBlock block) { assert !block.isLoopHeader; block.isLoopHeader = true; - if (block.isExceptionEntry) { + if (block.isExceptionEntry()) { // Loops that are implicitly formed by an exception handler lead to all sorts of // corner cases. // Don't compile such methods for now, until we see a concrete case that allows @@ -1567,9 +1647,14 @@ private boolean checkBlocks(int start, BciBlock inserting) { return true; } - @SuppressWarnings("try") public static BciBlockMapping create(BytecodeStream stream, Bytecode code, OptionValues options, DebugContext debug) { BciBlockMapping map = new BciBlockMapping(code, debug); + buildMap(stream, code, options, debug, map); + return map; + } + + @SuppressWarnings("try") + protected static void buildMap(BytecodeStream stream, Bytecode code, OptionValues options, DebugContext debug, BciBlockMapping map) { try (Scope scope = debug.scope("BciBlockMapping", map)) { map.build(stream, options); if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) { @@ -1578,8 +1663,6 @@ public static BciBlockMapping create(BytecodeStream stream, Bytecode code, Optio } catch (Throwable t) { throw debug.handle(t); } - - return map; } public BciBlock[] getLoopHeaders() { diff --git a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java index 0308c7d7a6a6..6e582123407e 100644 --- a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java +++ b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java @@ -616,12 +616,6 @@ protected void processPlaceholderFrameStates(boolean isCompilationRoot) { frameState.safeDelete(); } - /* - * To enable any needed modifications for the exception object with its new - * state, processInstruction must be called. - */ - parser.processInstruction(exceptionNode, dispatchState); - } else if (frameState.bci == BytecodeFrame.UNWIND_BCI) { if (graph.getGuardsStage().allowsFloatingGuards()) { throw GraalError.shouldNotReachHere("Cannot handle this UNWIND_BCI"); @@ -890,7 +884,7 @@ static boolean containsReturnValue(List list, ValueNode valu protected final OptionValues options; protected final DebugContext debug; - private BciBlockMapping blockMap; + protected BciBlockMapping blockMap; private LocalLiveness liveness; protected final int entryBCI; private final BytecodeParser parent; @@ -1018,6 +1012,10 @@ protected void buildRootMethod() { ComputeLoopFrequenciesClosure.compute(graph); } + protected BciBlockMapping generateBlockMap() { + return BciBlockMapping.create(stream, code, options, graph.getDebug()); + } + @SuppressWarnings("try") protected void build(FixedWithNextNode startInstruction, FrameStateBuilder startFrameState) { if (PrintProfilingInformation.getValue(options) && profilingInfo != null) { @@ -1033,8 +1031,7 @@ protected void build(FixedWithNextNode startInstruction, FrameStateBuilder start } // compute the block map, setup exception handlers and get the entrypoint(s) - BciBlockMapping newMapping = BciBlockMapping.create(stream, code, options, graph.getDebug()); - this.blockMap = newMapping; + this.blockMap = generateBlockMap(); this.firstInstructionArray = new FixedWithNextNode[blockMap.getBlockCount()]; this.entryStateArray = new FrameStateBuilder[blockMap.getBlockCount()]; if (!method.isStatic()) { @@ -1055,7 +1052,7 @@ protected void build(FixedWithNextNode startInstruction, FrameStateBuilder start } lastInstr = startInstruction; - this.setCurrentFrameState(startFrameState); + frameState = startFrameState; stream.setBCI(0); BciBlock startBlock = blockMap.getStartBlock(); @@ -1087,7 +1084,6 @@ protected void build(FixedWithNextNode startInstruction, FrameStateBuilder start try (DebugCloseable context = openNodeContext()) { if (method.isSynchronized()) { - finishPrepare(lastInstr, BytecodeFrame.BEFORE_BCI, frameState); // add a monitor enter to the start block methodSynchronizedObject = synchronizedObject(frameState, method); @@ -1102,8 +1098,6 @@ protected void build(FixedWithNextNode startInstruction, FrameStateBuilder start profilingPlugin.profileInvoke(this, method, stateBefore); } - finishPrepare(lastInstr, 0, frameState); - genInfoPointNode(InfopointReason.METHOD_START, null); } @@ -1144,16 +1138,6 @@ private boolean computeKindVerification(FrameStateBuilder startFrameState) { return true; } - /** - * Hook for subclasses to modify synthetic code (start nodes and unwind nodes). - * - * @param instruction the current last instruction - * @param bci the current bci - * @param state The current frame state. - */ - protected void finishPrepare(FixedWithNextNode instruction, int bci, FrameStateBuilder state) { - } - protected void cleanupFinalGraph() { GraphUtil.normalizeLoops(graph); @@ -1339,7 +1323,7 @@ private AbstractBeginNode handleException(ValueNode exceptionObject, int bci, bo dispatchState.setRethrowException(true); } this.controlFlowSplit = true; - FixedWithNextNode afterExceptionLoaded = processInstruction(dispatchBegin, dispatchState); + FixedWithNextNode afterExceptionLoaded = dispatchBegin; if (deoptimizeOnly) { DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter)); @@ -1364,7 +1348,7 @@ protected void createHandleExceptionTarget(FixedWithNextNode afterExceptionLoade * at the endBci yet, there is no exception handler for this bci and we can unwind * immediately. */ - if (bci != currentBlock.endBci || dispatchBlock == null) { + if (bci != currentBlock.getEndBci() || dispatchBlock == null) { dispatchBlock = blockMap.getUnwindBlock(); } @@ -2668,7 +2652,7 @@ private ValueNode processCalleeReturn(ResolvedJavaMethod targetMethod, InliningS } if (returnMergeNode != null) { returnMergeNode.setStateAfter(createFrameState(stream.nextBCI(), returnMergeNode)); - lastInstr = processInstruction(returnMergeNode, frameState); + lastInstr = returnMergeNode; } return calleeReturnValue; } @@ -2687,7 +2671,7 @@ protected InvokeNode createInvoke(int invokeBci, CallTargetNode callTarget, Java } protected InvokeWithExceptionNode createInvokeWithException(int invokeBci, CallTargetNode callTarget, JavaKind resultType, ExceptionEdgeAction exceptionEdgeAction) { - if (currentBlock != null && stream.nextBCI() > currentBlock.endBci) { + if (currentBlock != null && stream.nextBCI() > currentBlock.getEndBci()) { /* * Clear non-live locals early so that the exception handler entry gets the cleared * state. @@ -2798,9 +2782,6 @@ private void beforeReturn(ValueNode x, JavaKind kind) { append(new FinalFieldBarrierNode(entryBCI == INVOCATION_ENTRY_BCI ? originalReceiver : null)); } synchronizedEpilogue(BytecodeFrame.AFTER_BCI, x, kind); - if (method.isSynchronized()) { - finishPrepare(lastInstr, BytecodeFrame.AFTER_BCI, frameState); - } } protected MonitorEnterNode createMonitorEnterNode(ValueNode x, MonitorIdNode monitorId) { @@ -3224,7 +3205,6 @@ protected void processBlock(BciBlock block) { lastInstr = firstInstruction; frameState = getEntryState(block); - setCurrentFrameState(frameState); currentBlock = block; if (block != blockMap.getUnwindBlock() && !(block instanceof ExceptionDispatchBlock)) { @@ -3236,21 +3216,20 @@ protected void processBlock(BciBlock block) { } if (block == blockMap.getUnwindBlock()) { - handleUnwindBlock((ExceptionDispatchBlock) block); + handleUnwindBlock(); } else if (block instanceof ExceptionDispatchBlock) { createExceptionDispatch((ExceptionDispatchBlock) block); } else { - iterateBytecodesForBlock(block); + handleBytecodeBlock(block); } } } - private void handleUnwindBlock(ExceptionDispatchBlock block) { + private void handleUnwindBlock() { if (frameState.lockDepth(false) != 0) { throw bailout("unbalanced monitors: too few exits exiting frame"); } assert !frameState.rethrowException(); - finishPrepare(lastInstr, block.deoptBci, frameState); if (parent == null) { createUnwind(); } else { @@ -3298,9 +3277,8 @@ private void synchronizedEpilogue(int bci, ValueNode currentReturnValue, JavaKin } @SuppressWarnings("try") - private void createExceptionDispatch(ExceptionDispatchBlock block) { + protected void createExceptionDispatch(ExceptionDispatchBlock block) { try (DebugCloseable context = openNodeContext(frameState, BytecodeFrame.AFTER_EXCEPTION_BCI)) { - processInstruction(lastInstr); assert frameState.stackSize() == 1 : frameState; if (block.handler.isCatchAll()) { @@ -3354,18 +3332,19 @@ private void createExceptionDispatch(ExceptionDispatchBlock block) { } } - private void appendGoto(BciBlock successor) { + protected void appendGoto(BciBlock successor) { FixedNode targetInstr = createTarget(successor, frameState, true, true); if (lastInstr != null && lastInstr != targetInstr) { lastInstr.setNext(targetInstr); } } - @SuppressWarnings("try") - protected void iterateBytecodesForBlock(BciBlock block) { + protected void handleBytecodeBlock(BciBlock block) { if (block.isLoopHeader()) { - // Create the loop header block, which later will merge the backward branches of - // the loop. + /* + * Create the loop header block, which later will merge the backward branches of the + * loop. + */ controlFlowSplit = true; LoopBeginNode loopBegin = appendLoopBegin(this.lastInstr, block.startBci); lastInstr = loopBegin; @@ -3396,13 +3375,17 @@ protected void iterateBytecodesForBlock(BciBlock block) { assert lastInstr.next() == null : "instructions already appended at block " + block; debug.log(" frameState: %s", frameState); - processInstruction(lastInstr); + iterateBytecodesForBlock(block); + } + @SuppressWarnings("try") + protected void iterateBytecodesForBlock(BciBlock block) { + assert block.isInstructionBlock(); int endBCI = stream.endBCI(); stream.setBCI(block.startBci); int bci = block.startBci; - BytecodesParsed.add(debug, block.endBci - bci); + BytecodesParsed.add(debug, block.getEndBci() - bci); /* Reset line number for new block */ if (graphBuilderConfig.insertFullInfopoints()) { @@ -3450,9 +3433,8 @@ protected void iterateBytecodesForBlock(BciBlock block) { assert block == currentBlock; assert checkLastInstruction(); - processInstruction(lastInstr); if (bci < endBCI) { - if (bci > block.endBci) { + if (bci > block.getEndBci()) { assert !block.getSuccessor(0).isExceptionEntry(); assert block.numNormalSuccessors() == 1; // we fell through to the next block, add a goto and break @@ -3527,20 +3509,6 @@ private LoopBeginNode appendLoopBegin(FixedWithNextNode fixedWithNext, int start } } - /** - * Like {@link GraphBuilderContext#processInstruction(FixedWithNextNode)}, a hook for subclasses - * to add other instructions after the provided instruction. - * - * @param instr The instruction used to determine which instructions must be added. - * @param state The current state under which the node is processed. - * @return Returns either the last instruction added (if changes performed) or the provided - * instruction. - */ - protected FixedWithNextNode processInstruction(FixedWithNextNode instr, FrameStateBuilder state) { - /* By default, no additional processing is needed. */ - return instr; - } - private void genInfoPointNode(InfopointReason reason, ValueNode escapedReturnValue) { if (!parsingIntrinsic() && graphBuilderConfig.insertFullInfopoints()) { append(new FullInfopointNode(reason, createFrameState(bci(), null), escapedReturnValue)); @@ -3870,7 +3838,7 @@ private boolean gotoOrFallThroughAfterConstant(BciBlock block) { int currentBCI = stream.nextBCI(); stream.setBCI(currentBCI); int currentBC = stream.currentBC(); - return stream.currentBCI() > block.endBci || currentBC == Bytecodes.GOTO || currentBC == Bytecodes.GOTO_W; + return stream.currentBCI() > block.getEndBci() || currentBC == Bytecodes.GOTO || currentBC == Bytecodes.GOTO_W; } private boolean returnAfterConstant(BciBlock block) { @@ -3937,7 +3905,7 @@ public BailoutException bailout(String string) { private FrameState createFrameState(int bci, StateSplit forStateSplit) { assert !(forStateSplit instanceof BytecodeExceptionNode); - if (currentBlock != null && bci > currentBlock.endBci) { + if (currentBlock != null && bci > currentBlock.getEndBci()) { frameState.clearNonLiveLocals(currentBlock, liveness, false); } return frameState.create(bci, forStateSplit); @@ -3966,10 +3934,6 @@ protected NodeSourcePosition createBytecodePosition() { return bytecodePosition; } - public void setCurrentFrameState(FrameStateBuilder frameState) { - this.frameState = frameState; - } - protected final BytecodeStream getStream() { return stream; } @@ -3999,7 +3963,7 @@ public void loadLocalObject(int index) { int nextBCI = stream.nextBCI(); int nextBC = stream.readUByte(nextBCI); - if (nextBCI <= currentBlock.endBci && nextBC == Bytecodes.GETFIELD) { + if (nextBCI <= currentBlock.getEndBci() && nextBC == Bytecodes.GETFIELD) { stream.next(); try (DebugCloseable ignored = openNodeContext()) { genGetField(stream.readCPI(), Bytecodes.GETFIELD, value); @@ -4568,7 +4532,7 @@ protected void genInstanceOf(ResolvedJavaType resolvedType, ValueNode objectIn) int next = getStream().nextBCI(); int value = getStream().readUByte(next); - if (next <= currentBlock.endBci && (value == Bytecodes.IFEQ || value == Bytecodes.IFNE)) { + if (next <= currentBlock.getEndBci() && (value == Bytecodes.IFEQ || value == Bytecodes.IFNE)) { getStream().next(); try (DebugCloseable context = openNodeContext()) { BciBlock firstSucc = currentBlock.getSuccessor(0); diff --git a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/LocalLiveness.java b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/LocalLiveness.java index f69caad8f1e0..b531bc4f0a11 100644 --- a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/LocalLiveness.java +++ b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/LocalLiveness.java @@ -140,7 +140,8 @@ private static boolean traceIteration(DebugContext debug, int iteration) { private boolean traceEnd(DebugContext debug, BciBlock block, int blockID) { if (debug.isLogEnabled()) { - debug.logv(" end B%d [%d, %d] in: %s out: %s gen: %s kill: %s", block.getId(), block.startBci, block.endBci, debugLiveIn(blockID), debugLiveOut(blockID), debugLiveGen(blockID), + debug.logv(" end B%d [%d, %d] in: %s out: %s gen: %s kill: %s", block.getId(), block.startBci, block.getEndBci(), debugLiveIn(blockID), debugLiveOut(blockID), + debugLiveGen(blockID), debugLiveKill(blockID)); } return true; @@ -155,7 +156,8 @@ private boolean traceSuccessor(DebugContext debug, BciBlock sux) { private boolean traceStart(DebugContext debug, BciBlock block, int blockID) { if (debug.isLogEnabled()) { - debug.logv(" start B%d [%d, %d] in: %s out: %s gen: %s kill: %s", block.getId(), block.startBci, block.endBci, debugLiveIn(blockID), debugLiveOut(blockID), debugLiveGen(blockID), + debug.logv(" start B%d [%d, %d] in: %s out: %s gen: %s kill: %s", block.getId(), block.startBci, block.getEndBci(), debugLiveIn(blockID), debugLiveOut(blockID), + debugLiveGen(blockID), debugLiveKill(blockID)); } return true; @@ -222,13 +224,13 @@ private boolean traceStart(DebugContext debug, BciBlock block, int blockID) { protected abstract void storeOne(int blockID, int local); private void computeLocalLiveness(BytecodeStream stream, BciBlock block) { - if (block.isExceptionDispatch()) { + if (!block.isInstructionBlock()) { return; } int blockID = block.getId(); int localIndex; stream.setBCI(block.startBci); - while (stream.currentBCI() <= block.endBci) { + while (stream.currentBCI() <= block.getEndBci()) { switch (stream.currentBC()) { case LLOAD: case DLOAD: diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java index a58e84f4ca92..26cae9fa23f5 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java @@ -47,7 +47,6 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DynamicPiNode; import org.graalvm.compiler.nodes.FixedGuardNode; -import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.LogicNode; @@ -436,15 +435,6 @@ default AbstractBeginNode genExplicitExceptionEdge(BytecodeExceptionKind excepti default void replacePlugin(GeneratedInvocationPlugin plugin, ResolvedJavaMethod targetMethod, ValueNode[] args, PluginReplacementNode.ReplacementFunction replacementFunction) { throw GraalError.unimplemented(); } - - /** - * A hook for subclasses to add other instructions around the provided instruction. - * - * @param instr The instruction used to determine which instructions must be added. - */ - default void processInstruction(FixedWithNextNode instr) { - /* By default, no additional processing is needed. */ - } } class GraphBuilderContextUtil { diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java index b6b7ab53efc3..e9484661bb12 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java @@ -1097,7 +1097,6 @@ private void setAccessNodeResult(FixedWithNextNode node, GraphBuilderContext b) } else { b.add(node); } - b.processInstruction(node); } protected final void createUnsafeAccess(ValueNode value, GraphBuilderContext b, UnsafeNodeConstructor nodeConstructor) { @@ -1157,7 +1156,6 @@ protected final void createUnsafeAccess(ValueNode value, GraphBuilderContext b, b.push(unsafeAccessKind, graph.addOrUnique(phi)); } b.setStateAfter(merge); - b.processInstruction(merge); } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java index 26d30e81d5be..5fc6b97738af 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java @@ -792,11 +792,13 @@ private VirtualFrame constructTargetFrame(CodeInfoQueryResult targetInfo, FrameI int newEndOfParams = endOfParams; for (int idx = 0; idx < numValues; idx++) { ValueInfo targetValue = targetFrame.getValueInfos()[idx]; - if (targetValue.getKind() == JavaKind.Illegal) { + if (targetValue.getKind() == JavaKind.Illegal || targetValue.getType() == FrameInfoQueryResult.ValueType.ReservedRegister) { /* - * The target value is optimized out, e.g. at a position after the lifetime of a - * local variable. Actually we don't care what's the source value in this case, but - * most likely it's also "illegal". + * The target value is either optimized out, e.g. at a position after the lifetime + * of a local variable, or is a reserved register. In either situation, we don't + * care what the source value is. Optimized out values will not be restored, and for + * reserved registers the value will be automatically correct when execution resumes + * in the target frame. */ } else { ValueInfo sourceValue = sourceFrame.getValueInfos()[idx]; @@ -862,13 +864,6 @@ private VirtualFrame constructTargetFrame(CodeInfoQueryResult targetInfo, FrameI DeoptimizationCounters.counters().constantValueCount.inc(); break; - case ReservedRegister: - /* - * Nothing to do, the register will automatically be correct when execution - * resumes in the target frame. - */ - break; - default: /* * There must not be any other target value types because deoptimization diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphBuilderPhase.java index 5792ef8b04cd..735025604d25 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphBuilderPhase.java @@ -24,46 +24,43 @@ */ package com.oracle.svm.hosted.phases; -import java.util.HashMap; -import java.util.Map; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.graalvm.compiler.bytecode.Bytecode; +import org.graalvm.compiler.bytecode.BytecodeStream; +import org.graalvm.compiler.core.common.PermanentBailoutException; import org.graalvm.compiler.core.common.type.StampPair; -import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.java.BciBlockMapping; import org.graalvm.compiler.java.BytecodeParser; import org.graalvm.compiler.java.FrameStateBuilder; import org.graalvm.compiler.java.GraphBuilderPhase; -import org.graalvm.compiler.nodes.AbstractBeginNode; -import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.FrameState; -import org.graalvm.compiler.nodes.Invoke; -import org.graalvm.compiler.nodes.KillingBeginNode; -import org.graalvm.compiler.nodes.LoopBeginNode; -import org.graalvm.compiler.nodes.StateSplit; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; -import org.graalvm.compiler.nodes.java.ExceptionObjectNode; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; import org.graalvm.compiler.nodes.spi.CoreProviders; +import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.word.WordTypes; import com.oracle.svm.core.code.FrameInfoEncoder; import com.oracle.svm.core.graal.nodes.DeoptEntryNode; import com.oracle.svm.core.graal.nodes.DeoptProxyAnchorNode; -import com.oracle.svm.hosted.NativeImageGenerator; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.nodes.DeoptProxyNode; import com.oracle.svm.hosted.nodes.SubstrateMethodCallTargetNode; import com.oracle.svm.hosted.phases.SubstrateGraphBuilderPhase.SubstrateBytecodeParser; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.JavaTypeProfile; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -82,10 +79,7 @@ protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodePar class HostedBytecodeParser extends SubstrateBytecodeParser { - private static final DeoptEntryNode STICKY_DEOPT_ENTRY = new DeoptEntryNode(); - private int currentDeoptIndex; - private Map deoptEntries = new HashMap<>(); HostedBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) { super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, true); @@ -106,6 +100,19 @@ protected boolean stampFromValueForForcedPhis() { return true; } + @Override + protected BciBlockMapping generateBlockMap() { + if (isMethodDeoptTarget()) { + /* + * Need to add blocks representing where deoptimization entrypoint nodes will be + * inserted. + */ + return HostedBciBlockMapping.create(stream, code, options, graph.getDebug()); + } else { + return BciBlockMapping.create(stream, code, options, graph.getDebug()); + } + } + @Override protected void build(FixedWithNextNode startInstruction, FrameStateBuilder startFrameState) { super.build(startInstruction, startFrameState); @@ -117,14 +124,10 @@ protected void build(FixedWithNextNode startInstruction, FrameStateBuilder start if (getMethod().compilationInfo.isDeoptTarget()) { /* - * Remove dangling DeoptProxyNodes which remained after deletion of the corresponding - * DeoptEntryNodes. + * All DeoptProxyNodes should be valid. */ for (DeoptProxyNode deoptProxy : graph.getNodes(DeoptProxyNode.TYPE)) { - if (!deoptProxy.hasProxyPoint()) { - ValueNode originalValue = deoptProxy.getOriginalNode(); - deoptProxy.replaceAtUsagesAndDelete(originalValue); - } + assert deoptProxy.hasProxyPoint(); } } } @@ -134,6 +137,63 @@ public MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, Resolv return new SubstrateMethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, getMethod().getProfilingInfo(), bci()); } + @Override + protected void createExceptionDispatch(BciBlockMapping.ExceptionDispatchBlock block) { + if (block instanceof HostedBciBlockMapping.DeoptEntryInsertionPoint) { + /* + * If this block is an DeoptEntryInsertionPoint, then a DeoptEntry must be inserted. + * Afterwards, this block should jump to either the original ExceptionDispatchBlock or + * the UnwindBlock if there is no handler. + */ + assert block instanceof HostedBciBlockMapping.DeoptExceptionDispatchBlock; + insertDeoptNode((HostedBciBlockMapping.DeoptEntryInsertionPoint) block); + List successors = block.getSuccessors(); + assert successors.size() <= 1; + BciBlockMapping.BciBlock successor = successors.isEmpty() ? blockMap.getUnwindBlock() : successors.get(0); + appendGoto(successor); + } else { + super.createExceptionDispatch(block); + } + } + + @Override + protected void iterateBytecodesForBlock(BciBlockMapping.BciBlock block) { + if (block instanceof HostedBciBlockMapping.DeoptEntryInsertionPoint) { + /* + * If this block is an DeoptEntryInsertionPoint, then a DeoptEntry must be inserted. + * Afterwards, this block should jump to the original BciBlock. + */ + assert block instanceof HostedBciBlockMapping.DeoptBciBlock; + insertDeoptNode((HostedBciBlockMapping.DeoptEntryInsertionPoint) block); + assert block.getSuccessors().size() == 1; + appendGoto(block.getSuccessor(0)); + } else { + super.iterateBytecodesForBlock(block); + } + } + + /** + * Inserts either a DeoptEntryNode or DeoptProxyAnchorNode into the graph. + */ + private void insertDeoptNode(HostedBciBlockMapping.DeoptEntryInsertionPoint deopt) { + /* Ensuring current frameState matches the expectations of the DeoptEntryInsertionPoint. */ + if (deopt instanceof HostedBciBlockMapping.DeoptBciBlock) { + assert !frameState.rethrowException(); + } else { + assert deopt instanceof HostedBciBlockMapping.DeoptExceptionDispatchBlock; + assert frameState.rethrowException(); + } + + DeoptProxyAnchorNode deoptNode = graph.add(deopt.isProxy() ? new DeoptProxyAnchorNode() : new DeoptEntryNode()); + if (lastInstr != null) { + lastInstr.setNext(deoptNode); + } + lastInstr = deoptNode; + FrameState stateAfter = frameState.create(deopt.frameStateBci(), deoptNode); + deoptNode.setStateAfter(stateAfter); + insertProxies(deoptNode, frameState); + } + private void insertProxies(FixedNode deoptTarget, FrameStateBuilder state) { /* @@ -155,233 +215,350 @@ private ValueNode createProxyNode(ValueNode value, FixedNode deoptTarget) { return graph.addOrUniqueWithInputs(v); } +} + +/** + * To guarantee DeoptEntryNodes and DeoptProxyNodes are inserted at the correct positions, the bci + * block mapping creation must be augmented to identify these insertion points. + */ +final class HostedBciBlockMapping extends BciBlockMapping { + /** - * Insert a deopt entry for the graph's start node. + * Keep track of blocks inserted for DeoptEntryPoints so that characteristics of these blocks + * can be validated later within {@link #verify()}. */ - @Override - protected void finishPrepare(FixedWithNextNode startInstr, int bci, FrameStateBuilder state) { - super.finishPrepare(startInstr, bci, state); + private final Set insertedBlocks; - if (getMethod().compilationInfo.isDeoptEntry(bci, false, false)) { - DeoptEntryNode deoptEntry = append(new DeoptEntryNode()); - deoptEntry.setStateAfter(frameState.create(bci, deoptEntry)); - long encodedBci = FrameInfoEncoder.encodeBci(bci, false, false); - deoptEntries.put(encodedBci, deoptEntry); - insertProxies(deoptEntry, state); - } + private HostedBciBlockMapping(Bytecode code, DebugContext debug) { + super(code, debug); + insertedBlocks = new HashSet<>(); } /** - * Creates a DeoptEntryNode which corresponds to the state before executing an invoke. + * Marks places within the graph where either a DeoptEntryNode or DeoptProxyAnchorNode needs to + * be inserted. */ - private void addBeforeInvokeDeoptEntry(ResolvedJavaMethod targetMethod, int invokeBci, ValueNode[] args) { - FrameState stateAfter = frameState.create(invokeBci, getNonIntrinsicAncestor(), false, targetMethod.getSignature().toParameterKinds(!targetMethod.isStatic()), args); - assert !stateAfter.rethrowException() : "Shouldn't be rethrowing an exception."; - long encodedBci = FrameInfoEncoder.encodeBci(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException()); - if (!deoptEntries.containsKey(encodedBci)) { - DeoptEntryNode deoptEntry = graph.add(new DeoptEntryNode()); - deoptEntry.setStateAfter(stateAfter); - insertProxies(deoptEntry, frameState); - - lastInstr.setNext(deoptEntry); - lastInstr = deoptEntry; - - for (int i = 0; i < args.length; i++) { - args[i] = createProxyNode(args[i], deoptEntry); - } - } + interface DeoptEntryInsertionPoint { + + /* The deopt entry position this block represents. */ + int deoptEntryBci(); + + boolean duringCall(); + + boolean rethrowException(); + /* - * Ensure that no one registers a later state (after the replacement) with the same frame - * state. + * The bci for the stateAfter value for the node to be inserted at this block. Note that for + * DeoptProxyAnchorNodes this value may not be the same as the deoptEntryBci. */ - deoptEntries.put(encodedBci, STICKY_DEOPT_ENTRY); + int frameStateBci(); + + /* Whether this block represents a DeoptEntryNode or DeoptProxyAnchorNode. */ + boolean isProxy(); + + BciBlock asBlock(); } - @Override - protected Invoke createNonInlinedInvoke(ExceptionEdgeAction exceptionEdge, int invokeBci, ValueNode[] invokeArgs, ResolvedJavaMethod targetMethod, - InvokeKind invokeKind, JavaKind resultType, JavaType returnType, JavaTypeProfile profile) { + /** + * Represents a DeoptEntryInsertionPoint whose successor is a BciBlock. + */ + static final class DeoptBciBlock extends BciBlock implements DeoptEntryInsertionPoint { + public final int deoptEntryBci; + public final boolean isProxy; + + private DeoptBciBlock(int startBci, int deoptEntryBci, boolean isProxy) { + super(startBci, startBci); + this.deoptEntryBci = deoptEntryBci; + this.isProxy = isProxy; + } - if (!parsingIntrinsic() && getMethod().compilationInfo.isRegisteredDeoptEntry(invokeBci, false, false)) { - /* - * Since it possible for this method to be inlined during runtime compilation, a - * DeoptEntryNode must be registered for this invoke. Replacements use the frame state - * before the invoke for all nodes that need a state, i.e., we want to re-execute the - * whole replacement in case of deoptimization. The frame state used for the - * DeoptEntryNode needs to be the same state that will be used later on in the - * intrinsic. - */ - addBeforeInvokeDeoptEntry(targetMethod, invokeBci, invokeArgs); + static DeoptBciBlock createDeoptEntry(int bci) { + return new DeoptBciBlock(bci, bci, false); } - return super.createNonInlinedInvoke(exceptionEdge, invokeBci, invokeArgs, targetMethod, invokeKind, resultType, returnType, profile); - } - @Override - protected void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) { - if (getMethod().compilationInfo.isDeoptEntry(bci(), false, false)) { - assert NativeImageGenerator.nativeImageInlineDuringParsingEnabled() || calleeIntrinsicContext != null : "only inlining replacements when inline during parsing disabled"; - /* - * Replacements use the frame state before the invoke for all nodes that need a state, - * i.e., we want to re-execute the whole replacement in case of deoptimization. - * Therefore, we need to register a DeoptEntryNode before inlining the replacement. The - * frame state used for the DeoptEntryNode needs to be the same state that will be used - * later on in the intrinsic. - */ - addBeforeInvokeDeoptEntry(targetMethod, bci(), args); + static DeoptBciBlock createDeoptProxy(int successorBci, int deoptBci) { + return new DeoptBciBlock(successorBci, deoptBci, true); + } + + @Override + public void setEndBci(int bci) { + throw GraalError.shouldNotReachHere(); + } + + @Override + public boolean isInstructionBlock() { + return false; } - super.parseAndInlineCallee(targetMethod, args, calleeIntrinsicContext); + @Override + public int deoptEntryBci() { + return deoptEntryBci; + } + + @Override + public int frameStateBci() { + return getStartBci(); + } + + /* + * Proxies correspond to the frameState (duringCall && !rethrowException) + */ + @Override + public boolean duringCall() { + return isProxy; + } + + @Override + public boolean rethrowException() { + return false; + } + + @Override + public boolean isProxy() { + return isProxy; + } + + @Override + public BciBlock asBlock() { + return this; + } + + @Override + public String toString() { + return super.toString() + " (DeoptBciBlock)"; + } } /** - * Insert deopt entries after all state splits. - * - * @return the new DeoptEntryNode (if added) or the original instruction. + * Represents a DeoptEntryInsertionPoint whose successor is an ExceptionDispatchBlock. */ - private FixedWithNextNode appendDeoptEntry(FixedWithNextNode instr, FrameStateBuilder stateBuilder) { - if (getMethod().compilationInfo.isDeoptTarget() && !parsingIntrinsic()) { - FrameState stateAfter = null; - if (instr instanceof StateSplit && !(instr instanceof DeoptEntryNode)) { - /* - * The regular case: the instruction is a state split and we insert a DeoptEntryNode - * right after it. - */ - StateSplit stateSplit = (StateSplit) instr; - stateAfter = stateSplit.stateAfter(); - } else if (instr instanceof AbstractBeginNode) { - /* - * We are at a block begin. If the block predecessor is a LoopExitNode or an - * InvokeWithException (both are state splits), we didn't inserted a deopt entry - * yet. So we do it at the begin of a block. - * - * Note that this only happens if the LoopExitNode/InvokeWithException is the - * _single_ predcessor of this block. In case of multiple predecessors, the block - * starts with a MergeNode and this is handled like a regular case. - */ - Node predecessor = instr.predecessor(); - if (predecessor instanceof KillingBeginNode) { - /* - * This is between an InvokeWithException and the BlockPlaceholderNode. - */ - predecessor = predecessor.predecessor(); - } - if (predecessor instanceof StateSplit && !(predecessor instanceof DeoptEntryNode)) { - stateAfter = ((StateSplit) predecessor).stateAfter(); - } - } + static class DeoptExceptionDispatchBlock extends ExceptionDispatchBlock implements DeoptEntryInsertionPoint { + public final boolean isProxy; - boolean needsDeoptEntry = false; - boolean needsProxies = false; - if (stateAfter != null) { - if (getMethod().compilationInfo.isDeoptEntry(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException())) { - needsDeoptEntry = true; - needsProxies = true; - } else if (instr.predecessor() instanceof Invoke && getMethod().compilationInfo.isDeoptEntry(((Invoke) instr.predecessor()).bci(), true, false)) { - /* - * Invoke nodes can be implicit deoptimization entry points. But we cannot - * anchor proxy nodes on invocations: The invoke has two successors (normal and - * exception handler), and we need to proxy values at the beginning of both. - */ - needsProxies = true; - } else if (instr instanceof ExceptionObjectNode && getMethod().compilationInfo.isDeoptEntry(((ExceptionObjectNode) instr).stateAfter().bci, true, false)) { - /* - * The predecessor of the ExceptionObjectNode will be an Invoke, but the Invoke - * has not been created yet. So the check above for the predecessor does not - * trigger. - */ - needsProxies = true; - } - } + DeoptExceptionDispatchBlock(int bci, boolean isProxy) { + super(bci); + this.isProxy = isProxy; + } - if (needsProxies) { - long encodedBci = FrameInfoEncoder.encodeBci(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException()); - DeoptProxyAnchorNode existingDeoptEntry = deoptEntries.get(encodedBci); - if (existingDeoptEntry == null || (existingDeoptEntry != STICKY_DEOPT_ENTRY && instr instanceof AbstractMergeNode)) { - if (existingDeoptEntry != null) { - /* - * Some state splits (i.e. MergeNode and LoopExitNode) do not have a - * correspondent byte code. Therefore there can be a previously added deopt - * entry with the same BCI. For MergeNodes we replace the previous entry - * because the new frame state has less live locals. - */ - existingDeoptEntry.replaceAtUsages(null); - graph.removeFixed(existingDeoptEntry); - deoptEntries.remove(encodedBci); - - if (existingDeoptEntry instanceof DeoptEntryNode) { - /* - * We already had a DeoptEntryNode registered earlier for some reason, - * so be conservative and create one again (and not just a - * DeoptProxyAnchorNode). - */ - needsDeoptEntry = true; - } - } - assert !deoptEntries.containsKey(encodedBci) : "duplicate deopt entry for encoded BCI " + encodedBci; - DeoptProxyAnchorNode deoptEntry = createDeoptEntry(stateBuilder, stateAfter, !needsDeoptEntry); - - if (instr instanceof LoopBeginNode) { - /* - * Loop headers to not have their own bci. Never move a deopt entry for the - * loop header down, e.g., into a loop end (that might then end up to be - * dead code). - */ - deoptEntries.put(encodedBci, STICKY_DEOPT_ENTRY); - } else { - deoptEntries.put(encodedBci, deoptEntry); - } - - assert instr.next() == null : "cannot append instruction to instruction which isn't end (" + instr + "->" + instr.next() + ")"; - instr.setNext(deoptEntry); - return deoptEntry; - } - } + DeoptExceptionDispatchBlock(ExceptionDispatchBlock dispatch, int bci, boolean isProxy) { + super(dispatch.handler, bci); + this.isProxy = isProxy; } - return instr; - } - @Override - public void processInstruction(FixedWithNextNode instr) { /* - * Call processInstruction with frameState to add a deoptimization entry point, if needed. + * For DeoptExceptionDispatchBlocks the deoptEntryBci and frameStateBci are the same. */ - processInstruction(instr, frameState); + @Override + public int deoptEntryBci() { + return deoptBci; + } + + @Override + public int frameStateBci() { + return deoptBci; + } + + /* + * Proxies correspond to the frameState (duringCall && !rethrowException) + */ + @Override + public boolean duringCall() { + return isProxy; + } + + /* + * If not a proxy, then this block should correspond to the point after an exception object + * is added, but before it is dispatched. + */ + @Override + public boolean rethrowException() { + return !isProxy; + } + + @Override + public boolean isProxy() { + return isProxy; + } + + @Override + public BciBlock asBlock() { + return this; + } + + @Override + public String toString() { + return super.toString() + " (DeoptExceptionDispatchBlock)"; + } + } + + /** + * Creates a BciBlockMapping with blocks explicitly representing where DeoptEntryNodes and + * DeoptProxyAnchorNodes are to be inserted. + */ + public static BciBlockMapping create(BytecodeStream stream, Bytecode code, OptionValues options, DebugContext debug) { + BciBlockMapping map = new HostedBciBlockMapping(code, debug); + buildMap(stream, code, options, debug, map); + return map; } @Override - protected FixedWithNextNode processInstruction(FixedWithNextNode instr, FrameStateBuilder state) { + protected BciBlock getInstructionBlock(BciBlock[] blockMap, int bci) { /* - * If the instruction has a successor, remove and re-append it after all modifications have - * been performed. + * DeoptBciBlocks are not instruction blocks; they only represent places where + * DeoptEntryNodes and DeoptProxyAnchorNodes are to be inserted. For a given bci, if a + * DeoptBciBlock is found within the blockMap, then the bci's instruction block will be the + * block's successor. */ - FixedNode originalNext = instr.next(); - if (originalNext != null) { - instr.setNext(null); + BciBlock current = blockMap[bci]; + assert !(current instanceof ExceptionDispatchBlock); + if (current instanceof DeoptBciBlock) { + assert !(current.getSuccessor(0) instanceof DeoptBciBlock); + return current.getSuccessor(0); } + return current; + } + + /** + * Checking whether this bci corresponds to a deopt entry point. + */ + private boolean needsDeoptEntryBlock(int bci, boolean duringCall, boolean rethrowException) { + return ((HostedMethod) code.getMethod()).compilationInfo.isDeoptEntry(bci, duringCall, rethrowException); + } + /* A new block must be created for all places where a DeoptEntryNode will be inserted. */ + @Override + protected boolean isStartOfNewBlock(BciBlock[] blockMap, BciBlock current, int bci) { /* - * Call appendDeoptEntry to guarantee all needed deoptimization entrypoints have been added. + * Checking whether a DeoptEntryPoint will be created for this spot. */ - FixedWithNextNode endInstr = appendDeoptEntry(instr, state); - if (originalNext != null) { - endInstr.setNext(originalNext); + if (needsDeoptEntryBlock(bci, false, false)) { + return true; + } + + return super.isStartOfNewBlock(blockMap, current, bci); + } + + private void recordInsertedBlock(DeoptEntryInsertionPoint block) { + blocksNotYetAssignedId++; + assert insertedBlocks.add(block); + } + + /** + * Checks whether a DeoptProxyAnchorNode must be inserted for an invoke with an implicit + * deoptimization entrypoint. + */ + @Override + protected void addInvokeNormalSuccessor(BciBlock[] blockMap, int invokeBci, BciBlock sux) { + if (sux.isExceptionEntry()) { + throw new PermanentBailoutException("Exception handler can be reached by both normal and exceptional control flow"); } - if (instr == lastInstr && instr != endInstr) { - assert originalNext == null; + if (needsDeoptEntryBlock(invokeBci, true, false)) { /* - * Need to update last instruction if it changed. + * Because this invoke has an implicit deopt entry, it is necessary to insert proxies + * afterwards. + * + * If a DeoptEntryPoint is next, then no additional action needs to be taken, as it + * inserts the needed proxy inputs. Otherwise, a DeoptProxy must be inserted. */ - lastInstr = endInstr; + if (!(sux instanceof DeoptBciBlock)) { + DeoptBciBlock proxyBlock = DeoptBciBlock.createDeoptProxy(sux.getStartBci(), invokeBci); + recordInsertedBlock(proxyBlock); + getInstructionBlock(blockMap, invokeBci).addSuccessor(proxyBlock); + proxyBlock.addSuccessor(sux); + return; + } + } + super.addInvokeNormalSuccessor(blockMap, invokeBci, sux); + } - return super.processInstruction(endInstr, state); + /** + * Adding new {@link DeoptBciBlock} for a bci which needs an explicit DeoptEntryNode. + */ + @Override + protected BciBlock processNewBciBlock(BciBlock[] blockMap, int bci, BciBlock newBlock) { + if (needsDeoptEntryBlock(bci, false, false)) { + DeoptBciBlock deoptBciBlock = DeoptBciBlock.createDeoptEntry(bci); + recordInsertedBlock(deoptBciBlock); + deoptBciBlock.addSuccessor(newBlock); + + // overwriting with new DeoptEntryBlock + blockMap[bci] = deoptBciBlock; + return deoptBciBlock; + } + return newBlock; } - private DeoptProxyAnchorNode createDeoptEntry(FrameStateBuilder stateBuilder, FrameState stateAfter, boolean anchorOnly) { - DeoptProxyAnchorNode deoptEntry = graph.add(anchorOnly ? new DeoptProxyAnchorNode() : new DeoptEntryNode()); - deoptEntry.setStateAfter(stateAfter); + /** + * A deopt entry node is must be inserted after an exception object is created if either: + *

      + *
    • This point is an explicit deopt entry.
    • + *
    • This point follows an invoke with an implicit deopt entry. In this situation only a deopt + * proxy node is needed.
    • + *
    + */ + @Override + protected ExceptionDispatchBlock processNewExceptionDispatchBlock(int bci, boolean isInvoke, ExceptionDispatchBlock handler) { + boolean isExplictDeoptEntry = needsDeoptEntryBlock(bci, false, true); + if (isExplictDeoptEntry || (isInvoke && needsDeoptEntryBlock(bci, true, false))) { + boolean isProxy = !isExplictDeoptEntry; + DeoptExceptionDispatchBlock block; + if (handler == null) { + /* + * This means that this exception will be dispatched to the unwind block. In this + * case it is still necessary to create deopt entry node. + */ + block = new DeoptExceptionDispatchBlock(bci, isProxy); + } else { + block = new DeoptExceptionDispatchBlock(handler, bci, isProxy); + block.addSuccessor(handler); + } + recordInsertedBlock(block); + return block; + } + return handler; + } - insertProxies(deoptEntry, stateBuilder); - return deoptEntry; + /** + * DeoptEntryInsertionPoint blocks should have the following characteristics: + * + *
      + *
    • Be on a reachable path (i.e. have a block id >= 0).
    • + *
    • Correspond to a deoptimization entrypoint recorded within method.compilationInfo.
    • + *
    • DeoptProxies can only represent implicit deoptimizations.
    • + *
    • Have only one non-proxy DeoptEntryInsertionPoint for each encoded BCI.
    • + *
    • Have at most 1 successor.
    • + *
    • Not be duplicated.
    • + *
    + */ + @Override + protected boolean verify() { + Set coveredEncodedBcis = new HashSet<>(); + for (DeoptEntryInsertionPoint deopt : insertedBlocks) { + BciBlock block = deopt.asBlock(); + int bci = deopt.deoptEntryBci(); + boolean duringCall = deopt.duringCall(); + boolean rethrowException = deopt.rethrowException(); + if (block.getId() < 0) { + /* + * When using -H:+DeoptimizeAll DeoptInsertionPoints can be within unreachable + * paths. + */ + assert !((HostedMethod) code.getMethod()).compilationInfo.isRegisteredDeoptEntry(bci, duringCall, rethrowException); + + /* Other information about this block is irrelevant as it is unreachable. */ + continue; + } else { + assert needsDeoptEntryBlock(bci, duringCall, rethrowException); + } + assert deopt.isProxy() == duringCall : "deopt proxy nodes always represent implicit deopt entries from invokes."; + if (!deopt.isProxy()) { + assert coveredEncodedBcis.add(FrameInfoEncoder.encodeBci(bci, duringCall, rethrowException)) : "Deoptimization entry points must be unique."; + } + assert block.getSuccessorCount() <= 1 : "DeoptEntryInsertionPoint must have at most 1 successor"; + assert !block.isDuplicate() : "DeoptEntryInsertionPoint must be unique"; + } + return super.verify(); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java index 0d3efea3d405..15aba0e900cc 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java @@ -384,7 +384,7 @@ private static boolean isDeoptimizationEnabled() { return DeoptimizationSupport.enabled() && !SubstrateUtil.isBuildingLibgraal(); } - private boolean isMethodDeoptTarget() { + protected boolean isMethodDeoptTarget() { return method instanceof SharedMethod && ((SharedMethod) method).isDeoptTarget(); } From e1a1564f9b6edc6630d08ff75fd5fe1e0c92f5bb Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 13 May 2021 12:10:21 +0200 Subject: [PATCH 241/290] group non-static fields together --- .../redefinition/plugins/impl/ExternalPluginHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java index 86a70eb973de..4e7f7035d537 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/plugins/impl/ExternalPluginHandler.java @@ -33,10 +33,10 @@ final class ExternalPluginHandler { - private final InteropLibrary interopLibrary; private static final String RERUN_CLINIT = "shouldRerunClassInitializer"; private static final String POST_HOTSWAP = "postHotSwap"; + private final InteropLibrary interopLibrary; private final StaticObject guestHandler; private ExternalPluginHandler(StaticObject handler, InteropLibrary library) { From e297bcb4ae07436aa0d1881fc11dfdead449fa53 Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 13 May 2021 12:13:50 +0200 Subject: [PATCH 242/290] Simplify patching inner class names and avoid making a loader constraint method public --- .../truffle/espresso/impl/ClassRegistries.java | 2 +- .../truffle/espresso/impl/ClassRegistry.java | 15 +++++++++++---- .../oracle/truffle/espresso/impl/ObjectKlass.java | 4 ++-- .../espresso/redefinition/ClassRedefinition.java | 14 ++++++-------- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java index ecb87223813a..3abd0ccb8acf 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistries.java @@ -241,7 +241,7 @@ void recordConstraint(Symbol type, Klass klass, StaticObject loader) { } } - public void removeUnloadedKlassConstraint(Klass klass, Symbol type) { + void removeUnloadedKlassConstraint(Klass klass, Symbol type) { assert klass.isInstanceClass(); constraints.removeUnloadedKlassConstraint(klass, type); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java index be157750eb99..84a6ac826901 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ClassRegistry.java @@ -374,11 +374,18 @@ private ObjectKlass loadKlassRecursively(Meta meta, Symbol type, boolean n return (ObjectKlass) klass; } - public void onClassRenamed(ObjectKlass oldKlass, Symbol newName, Symbol type) { - Symbol newType = context.getTypes().fromName(newName); - classes.put(newType, new ClassRegistries.RegistryEntry(oldKlass)); + public void onClassRenamed(ObjectKlass renamedKlass) { + // First remove class loader constraint if newType was previously loaded. + // That class instance will either be assigned a patched name, or marked + // as removed. + Klass loadedKlass = findLoadedKlass(renamedKlass.getType()); + if (loadedKlass != null) { + context.getRegistries().removeUnloadedKlassConstraint(loadedKlass, renamedKlass.getType()); + } + + classes.put(renamedKlass.getType(), new ClassRegistries.RegistryEntry(renamedKlass)); // record the new loading constraint - context.getRegistries().recordConstraint(type, oldKlass, oldKlass.getDefiningClassLoader()); + context.getRegistries().recordConstraint(renamedKlass.getType(), renamedKlass, renamedKlass.getDefiningClassLoader()); } public void onInnerClassRemoved(Symbol type) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index 21efec093a60..c6ab08320cf2 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -1293,9 +1293,9 @@ private static boolean isVirtual(ParserMethod m) { return !Modifier.isStatic(m.getFlags()) && !Modifier.isPrivate(m.getFlags()) && !Name._init_.equals(m.getName()); } - public void patchClassName(Symbol newName) { + public void patchClassName(Symbol newName, Symbol newType) { name = newName; - type = getContext().getTypes().fromName(newName); + type = newType; } public void removeByRedefinition() { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java index 0b9b5fb5c088..ba044b7d069c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/redefinition/ClassRedefinition.java @@ -647,7 +647,6 @@ private static boolean isUnchangedField(Field oldField, ParserField newField) { private void doRedefineClass(ChangePacket packet, List refreshSubClasses) { ObjectKlass oldKlass = packet.info.getKlass(); - ClassRegistry classRegistry = context.getRegistries().getClassRegistry(packet.info.getClassLoader()); if (packet.info.isRenamed()) { // renaming a class is done by // 1. Rename the 'name' and 'type' Symbols in the Klass @@ -656,13 +655,12 @@ private void doRedefineClass(ChangePacket packet, List refreshSubCl // 4. update the JDWP refType ID for the klass instance // 5. replace/record a classloader constraint for the new type and klass combination - Symbol type = context.getTypes().fromName(packet.info.getName()); - Klass loadedKlass = classRegistry.findLoadedKlass(type); - if (loadedKlass != null) { - context.getRegistries().removeUnloadedKlassConstraint(loadedKlass, type); - } - oldKlass.patchClassName(packet.info.getName()); - classRegistry.onClassRenamed(oldKlass, packet.info.getName(), type); + Symbol newName = packet.info.getName(); + Symbol newType = context.getTypes().fromName(newName); + + oldKlass.patchClassName(newName, newType); + ClassRegistry classRegistry = context.getRegistries().getClassRegistry(packet.info.getClassLoader()); + classRegistry.onClassRenamed(oldKlass); InterpreterToVM.setFieldObject(StaticObject.NULL, oldKlass.mirror(), context.getMeta().java_lang_Class_name); } From 596650fd25bfe811afe4ed72e83876adf74e38db Mon Sep 17 00:00:00 2001 From: Jan Stola Date: Thu, 13 May 2021 16:16:15 +0200 Subject: [PATCH 243/290] Memory allocation failure should not be an internal error. --- .../org/graalvm/wasm/exception/Failure.java | 1 + .../wasm/memory/ByteArrayWasmMemory.java | 24 ++++++++++++------- .../graalvm/wasm/memory/UnsafeWasmMemory.java | 24 ++++++++++++------- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java index a6f41525eb38..e9820052b44b 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java @@ -122,6 +122,7 @@ public enum Failure { MEMORY_INSTANCE_SIZE_LIMIT_EXCEEDED(Type.TRAP, "memory instance size exceeds limit"), CALL_STACK_EXHAUSTED(Type.EXHAUSTION, "call stack exhausted"), + MEMORY_ALLOCATION_FAILED(Type.EXHAUSTION, "could not allocate memory"), // TODO(mbovel): replace UNSPECIFIED_INTERNAL usages with assertInternal/shouldNotReachHere. UNSPECIFIED_INTERNAL(Type.INTERNAL, "unspecified"); diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java index d06ae0f6e6fe..87f6cc4283cf 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java @@ -89,7 +89,11 @@ private ByteArrayWasmMemory(int declaredMinSize, int declaredMaxSize, int initia this.declaredMinSize = declaredMinSize; this.declaredMaxSize = declaredMaxSize; this.maxAllowedSize = maxAllowedSize; - this.buffer = new byte[initialSize * MEMORY_PAGE_SIZE]; + try { + this.buffer = new byte[initialSize * MEMORY_PAGE_SIZE]; + } catch (OutOfMemoryError error) { + throw WasmException.create(Failure.MEMORY_ALLOCATION_FAILED); + } } public ByteArrayWasmMemory(int declaredMinSize, int declaredMaxSize, int maxAllowedSize) { @@ -139,13 +143,17 @@ public synchronized boolean grow(int extraPageSize) { if (extraPageSize == 0) { return true; } else if (compareUnsigned(extraPageSize, maxAllowedSize) <= 0 && compareUnsigned(size() + extraPageSize, maxAllowedSize) <= 0) { - // Condition above and limit on maxPageSize (see ModuleLimits#MAX_MEMORY_SIZE) ensure - // computation of targetByteSize does not overflow. - final int targetByteSize = multiplyExact(addExact(size(), extraPageSize), MEMORY_PAGE_SIZE); - final byte[] newBuffer = new byte[targetByteSize]; - System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); - buffer = newBuffer; - return true; + try { + // Condition above and limit on maxPageSize (see ModuleLimits#MAX_MEMORY_SIZE) + // ensure computation of targetByteSize does not overflow. + final int targetByteSize = multiplyExact(addExact(size(), extraPageSize), MEMORY_PAGE_SIZE); + final byte[] newBuffer = new byte[targetByteSize]; + System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; + return true; + } catch (OutOfMemoryError error) { + throw WasmException.create(Failure.MEMORY_ALLOCATION_FAILED); + } } else { return false; } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java index 79b05c21fc11..3e2ec5aa7315 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java @@ -102,7 +102,11 @@ private UnsafeWasmMemory(int declaredMinSize, int declaredMaxSize, int initialSi this.size = declaredMinSize; this.maxAllowedSize = maxAllowedSize; final long byteSize = byteSize(); - this.startAddress = unsafe.allocateMemory(byteSize); + try { + this.startAddress = unsafe.allocateMemory(byteSize); + } catch (OutOfMemoryError error) { + throw WasmException.create(Failure.MEMORY_ALLOCATION_FAILED); + } unsafe.setMemory(startAddress, byteSize, (byte) 0); } @@ -165,13 +169,17 @@ public boolean grow(int extraPageSize) { // Condition above and limit on maxPageSize (see ModuleLimits#MAX_MEMORY_SIZE) ensure // computation of targetByteSize does not overflow. final int targetByteSize = multiplyExact(addExact(size(), extraPageSize), MEMORY_PAGE_SIZE); - final long updatedStartAddress = unsafe.allocateMemory(targetByteSize); - unsafe.copyMemory(startAddress, updatedStartAddress, byteSize()); - unsafe.setMemory(updatedStartAddress + byteSize(), targetByteSize - byteSize(), (byte) 0); - unsafe.freeMemory(startAddress); - startAddress = updatedStartAddress; - size += extraPageSize; - return true; + try { + final long updatedStartAddress = unsafe.allocateMemory(targetByteSize); + unsafe.copyMemory(startAddress, updatedStartAddress, byteSize()); + unsafe.setMemory(updatedStartAddress + byteSize(), targetByteSize - byteSize(), (byte) 0); + unsafe.freeMemory(startAddress); + startAddress = updatedStartAddress; + size += extraPageSize; + return true; + } catch (OutOfMemoryError error) { + throw WasmException.create(Failure.MEMORY_ALLOCATION_FAILED); + } } else { return false; } From a15800055a1f42f572e49322eabfe6b7acf52ecd Mon Sep 17 00:00:00 2001 From: Jan Stola Date: Thu, 13 May 2021 16:18:40 +0200 Subject: [PATCH 244/290] Adding a regression test of memory allocation failure. --- .../src/org/graalvm/wasm/test/WasmJsApiSuite.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmJsApiSuite.java b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmJsApiSuite.java index 8d99e15745f2..73a254a98bb2 100644 --- a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmJsApiSuite.java +++ b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmJsApiSuite.java @@ -81,6 +81,7 @@ import org.junit.Test; import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.ExceptionType; @@ -779,6 +780,20 @@ public void testTableImport() throws IOException, InterruptedException { }); } + @Test + public void testMemoryAllocationFailure() { + // Memory allocation should either succeed or throw an interop + // exception (not an internal error like OutOfMemoryError). + try { + Object[] memories = new Object[5]; + for (int i = 0; i < memories.length; i++) { + memories[i] = Memory.create(new MemoryDescriptor(32767, 32767)); + } + } catch (AbstractTruffleException ex) { + Assert.assertTrue("Should throw interop exception", InteropLibrary.getUncached(ex).isException(ex)); + } + } + private static void runTest(Consumer testCase) throws IOException { final Context.Builder contextBuilder = Context.newBuilder("wasm"); contextBuilder.option("wasm.Builtins", "testutil:testutil"); From 0ffe2cf53a5b95a88745288671ee070f4c5cbdfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Sun, 9 May 2021 16:32:51 +0200 Subject: [PATCH 245/290] Simplify array load/store bytecodes. --- .../truffle/espresso/nodes/BytecodeNode.java | 129 ++++++------------ 1 file changed, 45 insertions(+), 84 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 93c42454539d..5af68ac93713 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -1338,39 +1338,6 @@ private static boolean takeBranch(long[] primitives, Object[] refs, int top, int // @formatter:on } - private static JavaKind arrayAccessKind(int opcode) { - assert (IALOAD <= opcode && opcode <= SALOAD) || (IASTORE <= opcode && opcode <= SASTORE); - switch (opcode) { - case IALOAD: // fall through - case IASTORE: - return JavaKind.Int; - case LALOAD: // fall through - case LASTORE: - return JavaKind.Long; - case FALOAD: // fall through - case FASTORE: - return JavaKind.Float; - case DALOAD: // fall through - case DASTORE: - return JavaKind.Double; - case AALOAD: // fall through - case AASTORE: - return JavaKind.Object; - case BALOAD: // fall through - case BASTORE: - return JavaKind.Byte; // or Boolean - case CALOAD: // fall through - case CASTORE: - return JavaKind.Char; - case SALOAD: // fall through - case SASTORE: - return JavaKind.Short; - default: - CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere(); - } - } - private void arrayLength(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI) { StaticObject array = nullCheck(popObject(refs, top - 1)); if (noForeignObjects.isValid() || array.isEspressoObject()) { @@ -1386,21 +1353,20 @@ private void arrayLength(VirtualFrame frame, long[] primitives, Object[] refs, i private void arrayLoad(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int loadOpcode) { assert IALOAD <= loadOpcode && loadOpcode <= SALOAD; - JavaKind kind = arrayAccessKind(loadOpcode); - CompilerAsserts.partialEvaluationConstant(kind); + CompilerAsserts.partialEvaluationConstant(loadOpcode); int index = popInt(primitives, top - 1); StaticObject array = nullCheck(popObject(refs, top - 2)); if (noForeignObjects.isValid() || array.isEspressoObject()) { // @formatter:off - switch (kind) { - case Byte: putInt(primitives, top - 2, getInterpreterToVM().getArrayByte(index, array, this)); break; - case Short: putInt(primitives, top - 2, getInterpreterToVM().getArrayShort(index, array, this)); break; - case Char: putInt(primitives, top - 2, getInterpreterToVM().getArrayChar(index, array, this)); break; - case Int: putInt(primitives, top - 2, getInterpreterToVM().getArrayInt(index, array, this)); break; - case Float: putFloat(primitives, top - 2, getInterpreterToVM().getArrayFloat(index, array, this)); break; - case Long: putLong(primitives, top - 2, getInterpreterToVM().getArrayLong(index, array, this)); break; - case Double: putDouble(primitives, top - 2, getInterpreterToVM().getArrayDouble(index, array, this)); break; - case Object: putObject(refs, top - 2, getInterpreterToVM().getArrayObject(index, array, this)); break; + switch (loadOpcode) { + case BALOAD: putInt(primitives, top - 2, getInterpreterToVM().getArrayByte(index, array, this)); break; + case SALOAD: putInt(primitives, top - 2, getInterpreterToVM().getArrayShort(index, array, this)); break; + case CALOAD: putInt(primitives, top - 2, getInterpreterToVM().getArrayChar(index, array, this)); break; + case IALOAD: putInt(primitives, top - 2, getInterpreterToVM().getArrayInt(index, array, this)); break; + case FALOAD: putFloat(primitives, top - 2, getInterpreterToVM().getArrayFloat(index, array, this)); break; + case LALOAD: putLong(primitives, top - 2, getInterpreterToVM().getArrayLong(index, array, this)); break; + case DALOAD: putDouble(primitives, top - 2, getInterpreterToVM().getArrayDouble(index, array, this)); break; + case AALOAD: putObject(refs, top - 2, getInterpreterToVM().getArrayObject(index, array, this)); break; default: CompilerDirectives.transferToInterpreter(); throw EspressoError.shouldNotReachHere(); @@ -1412,28 +1378,27 @@ private void arrayLoad(VirtualFrame frame, long[] primitives, Object[] refs, int putInt(primitives, top - 1, index); putObject(refs, top - 2, array); // The stack effect difference vs. original bytecode is always 0. - quickenArrayLoad(frame, primitives, refs, top, curBCI, loadOpcode, kind); + quickenArrayLoad(frame, primitives, refs, top, curBCI, loadOpcode); } } private void arrayStore(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int storeOpcode) { assert IASTORE <= storeOpcode && storeOpcode <= SASTORE; - JavaKind kind = arrayAccessKind(storeOpcode); - CompilerAsserts.partialEvaluationConstant(kind); - int offset = kind.needsTwoSlots() ? 2 : 1; + CompilerAsserts.partialEvaluationConstant(storeOpcode); + int offset = (storeOpcode == LASTORE || storeOpcode == DASTORE) ? 2 : 1; int index = popInt(primitives, top - 1 - offset); StaticObject array = nullCheck(popObject(refs, top - 2 - offset)); if (noForeignObjects.isValid() || array.isEspressoObject()) { // @formatter:off - switch (kind) { - case Byte: getInterpreterToVM().setArrayByte((byte) popInt(primitives, top - 1), index, array, this); break; - case Short: getInterpreterToVM().setArrayShort((short) popInt(primitives, top - 1), index, array, this); break; - case Char: getInterpreterToVM().setArrayChar((char) popInt(primitives, top - 1), index, array, this); break; - case Int: getInterpreterToVM().setArrayInt(popInt(primitives, top - 1), index, array, this); break; - case Float: getInterpreterToVM().setArrayFloat(popFloat(primitives, top - 1), index, array, this); break; - case Long: getInterpreterToVM().setArrayLong(popLong(primitives, top - 1), index, array, this); break; - case Double: getInterpreterToVM().setArrayDouble(popDouble(primitives, top - 1), index, array, this); break; - case Object: referenceArrayStore(refs, top, index, array); break; + switch (storeOpcode) { + case BASTORE: getInterpreterToVM().setArrayByte((byte) popInt(primitives, top - 1), index, array, this); break; + case SASTORE: getInterpreterToVM().setArrayShort((short) popInt(primitives, top - 1), index, array, this); break; + case CASTORE: getInterpreterToVM().setArrayChar((char) popInt(primitives, top - 1), index, array, this); break; + case IASTORE: getInterpreterToVM().setArrayInt(popInt(primitives, top - 1), index, array, this); break; + case FASTORE: getInterpreterToVM().setArrayFloat(popFloat(primitives, top - 1), index, array, this); break; + case LASTORE: getInterpreterToVM().setArrayLong(popLong(primitives, top - 1), index, array, this); break; + case DASTORE: getInterpreterToVM().setArrayDouble(popDouble(primitives, top - 1), index, array, this); break; + case AASTORE: referenceArrayStore(refs, top, index, array); break; default: CompilerDirectives.transferToInterpreter(); throw EspressoError.shouldNotReachHere(); @@ -1445,7 +1410,7 @@ private void arrayStore(VirtualFrame frame, long[] primitives, Object[] refs, in putInt(primitives, top - 1 - offset, index); putObject(refs, top - 2 - offset, array); // The stack effect difference vs. original bytecode is always 0. - quickenArrayStore(frame, primitives, refs, top, curBCI, storeOpcode, kind); + quickenArrayStore(frame, primitives, refs, top, curBCI, storeOpcode); } } @@ -1715,26 +1680,24 @@ private int quickenArrayLength(VirtualFrame frame, long[] primitives, Object[] r return arrayLengthNode.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(ARRAYLENGTH); } - private int quickenArrayLoad(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int loadOpcode, JavaKind componentKind) { + private int quickenArrayLoad(VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int loadOpcode) { CompilerDirectives.transferToInterpreterAndInvalidate(); + assert IALOAD <= loadOpcode && loadOpcode <= SALOAD; BaseQuickNode arrayLoadNode; synchronized (this) { if (bs.currentVolatileBC(curBCI) == SLIM_QUICK) { arrayLoadNode = sparseNodes[curBCI]; } else { // @formatter:off - switch (componentKind) { - case Boolean: // fall-through - case Byte: - arrayLoadNode = ByteArrayLoadNodeGen.create(top, curBCI); - break; - case Short : arrayLoadNode = ShortArrayLoadNodeGen.create(top, curBCI); break; - case Char : arrayLoadNode = CharArrayLoadNodeGen.create(top, curBCI); break; - case Int : arrayLoadNode = IntArrayLoadNodeGen.create(top, curBCI); break; - case Float : arrayLoadNode = FloatArrayLoadNodeGen.create(top, curBCI); break; - case Long : arrayLoadNode = LongArrayLoadNodeGen.create(top, curBCI); break; - case Double : arrayLoadNode = DoubleArrayLoadNodeGen.create(top, curBCI); break; - case Object : arrayLoadNode = ReferenceArrayLoadNodeGen.create(top, curBCI); break; + switch (loadOpcode) { + case BALOAD: arrayLoadNode = ByteArrayLoadNodeGen.create(top, curBCI); break; + case SALOAD: arrayLoadNode = ShortArrayLoadNodeGen.create(top, curBCI); break; + case CALOAD: arrayLoadNode = CharArrayLoadNodeGen.create(top, curBCI); break; + case IALOAD: arrayLoadNode = IntArrayLoadNodeGen.create(top, curBCI); break; + case FALOAD: arrayLoadNode = FloatArrayLoadNodeGen.create(top, curBCI); break; + case LALOAD: arrayLoadNode = LongArrayLoadNodeGen.create(top, curBCI); break; + case DALOAD: arrayLoadNode = DoubleArrayLoadNodeGen.create(top, curBCI); break; + case AALOAD: arrayLoadNode = ReferenceArrayLoadNodeGen.create(top, curBCI); break; default: CompilerDirectives.transferToInterpreter(); throw EspressoError.shouldNotReachHere("unexpected kind"); @@ -1746,26 +1709,24 @@ private int quickenArrayLoad(VirtualFrame frame, long[] primitives, Object[] ref return arrayLoadNode.execute(frame, primitives, refs) - Bytecodes.stackEffectOf(loadOpcode); } - private int quickenArrayStore(final VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int storeOpcode, JavaKind componentKind) { + private int quickenArrayStore(final VirtualFrame frame, long[] primitives, Object[] refs, int top, int curBCI, int storeOpcode) { CompilerDirectives.transferToInterpreterAndInvalidate(); + assert IASTORE <= storeOpcode && storeOpcode <= SASTORE; BaseQuickNode arrayStoreNode; synchronized (this) { if (bs.currentVolatileBC(curBCI) == SLIM_QUICK) { arrayStoreNode = sparseNodes[curBCI]; } else { // @formatter:off - switch (componentKind) { - case Boolean: // fall-through - case Byte: - arrayStoreNode = ByteArrayStoreNodeGen.create(top, curBCI); - break; - case Short : arrayStoreNode = ShortArrayStoreNodeGen.create(top, curBCI); break; - case Char : arrayStoreNode = CharArrayStoreNodeGen.create(top, curBCI); break; - case Int : arrayStoreNode = IntArrayStoreNodeGen.create(top, curBCI); break; - case Float : arrayStoreNode = FloatArrayStoreNodeGen.create(top, curBCI); break; - case Long : arrayStoreNode = LongArrayStoreNodeGen.create(top, curBCI); break; - case Double : arrayStoreNode = DoubleArrayStoreNodeGen.create(top, curBCI); break; - case Object : arrayStoreNode = ReferenceArrayStoreNodeGen.create(top, curBCI); break; + switch (storeOpcode) { + case BASTORE: arrayStoreNode = ByteArrayStoreNodeGen.create(top, curBCI); break; + case SASTORE: arrayStoreNode = ShortArrayStoreNodeGen.create(top, curBCI); break; + case CASTORE: arrayStoreNode = CharArrayStoreNodeGen.create(top, curBCI); break; + case IASTORE: arrayStoreNode = IntArrayStoreNodeGen.create(top, curBCI); break; + case FASTORE: arrayStoreNode = FloatArrayStoreNodeGen.create(top, curBCI); break; + case LASTORE: arrayStoreNode = LongArrayStoreNodeGen.create(top, curBCI); break; + case DASTORE: arrayStoreNode = DoubleArrayStoreNodeGen.create(top, curBCI); break; + case AASTORE: arrayStoreNode = ReferenceArrayStoreNodeGen.create(top, curBCI); break; default: CompilerDirectives.transferToInterpreter(); throw EspressoError.shouldNotReachHere("unexpected kind"); From 89f1a4533786b86df054f72f8b663c00a975f5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Tue, 11 May 2021 17:25:51 +0200 Subject: [PATCH 246/290] Add explicit accessors in BytecodeStream to handle wide and short bytecodes. --- .../espresso/bytecode/BytecodeStream.java | 119 +++++++++++++----- 1 file changed, 88 insertions(+), 31 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/bytecode/BytecodeStream.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/bytecode/BytecodeStream.java index d022e5d83481..758285a755e7 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/bytecode/BytecodeStream.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/bytecode/BytecodeStream.java @@ -39,6 +39,8 @@ * A utility class that makes iterating over bytecodes and reading operands simpler and less error * prone. For example, it handles the {@link Bytecodes#WIDE} instruction and wide variants of * instructions internally. + * + * Some operations have suffix wi */ public final class BytecodeStream { @@ -61,11 +63,7 @@ public BytecodeStream(final byte[] code) { * @return the next bytecode index */ public int nextBCI(int curBCI) { - if (curBCI < code.length) { - return curBCI + lengthOf(curBCI); - } else { - return curBCI; - } + return curBCI + lengthOf(curBCI); } /** @@ -81,7 +79,8 @@ public int endBCI() { * Gets the current opcode. This method will never return the {@link Bytecodes#WIDE WIDE} * opcode, but will instead return the opcode that is modified by the {@code WIDE} opcode. * - * @return the current opcode; {@link Bytecodes#END} if at or beyond the end of the code + * @return the current opcode; + * @see #opcode(int) */ public int currentBC(int curBCI) { int opcode = opcode(curBCI); @@ -123,7 +122,29 @@ public int readLocalIndex(int curBCI) { } /** - * Read the delta for an {@link Bytecodes#IINC} bytecode. + * Reads the index of a local variable for one of the load or store instructions. The + * {@link Bytecodes#WIDE} modifier is handled internally. + * + * @return the index of the local variable + */ + public int readLocalIndex1(int curBCI) { + // read local variable index for load/store + return Bytes.beU1(code, curBCI + 1); + } + + /** + * Reads the index of a local variable for one of the load or store instructions. The + * {@link Bytecodes#WIDE} modifier is NOT handled internally. + * + * @return the index of the local variable + */ + public int readLocalIndex2(int curBCI) { + // read local variable index for load/store + return Bytes.beU2(code, curBCI + 2); + } + + /** + * Read the delta for an {@link Bytecodes#IINC} bytecode. The {@link Bytecodes#WIDE} is handled. * * @return the delta for the {@code IINC} */ @@ -136,7 +157,29 @@ public int readIncrement(int curBCI) { } /** - * Read the destination of a {@link Bytecodes#GOTO} or {@code IF} instructions. + * Read the delta for an {@link Bytecodes#IINC} bytecode. The {@link Bytecodes#WIDE} modifier is + * NOThandled internally. + * + * @return the delta for the {@code IINC} + */ + public int readIncrement1(int curBCI) { + // read the delta for the iinc bytecode + return Bytes.beS1(code, curBCI + 2); + } + + /** + * Read the delta for a {@link Bytecodes#WIDE} + {@link Bytecodes#IINC} bytecode. + * + * @return the delta for the {@code WIDE IINC} + */ + public int readIncrement2(int curBCI) { + // read the delta for the iinc bytecode + return Bytes.beS2(code, curBCI + 4); + } + + /** + * Read the destination of a {@link Bytecodes#GOTO} or {@code IF} instructions. Wide bytecodes: + * {@link Bytecodes#GOTO_W} {@link Bytecodes#JSR_W}, are handled internally. * * @return the destination bytecode index */ @@ -150,6 +193,26 @@ public int readBranchDest(int curBCI) { } } + /** + * Read the destination of a {@link Bytecodes#GOTO_W} or {@code JSR_W} instructions. + * + * @return the destination bytecode index + */ + public int readBranchDest4(int curBCI) { + // reads the destination for a branch bytecode + return curBCI + Bytes.beS4(code, curBCI + 1); + } + + /** + * Read the destination of a {@link Bytecodes#GOTO} or {@code IF} instructions. + * + * @return the destination bytecode index + */ + public int readBranchDest2(int curBCI) { + // reads the destination for a branch bytecode + return curBCI + Bytes.beS2(code, curBCI + 1); + } + /** * Read a signed 4-byte integer from the bytecode stream at the specified bytecode index. * @@ -172,7 +235,8 @@ public int readUByte(int bci) { } /** - * Reads a constant pool index for the current instruction. + * Reads a constant pool index for the current instruction. Wide and short bytecodes are handled + * internally. * * @return the constant pool index */ @@ -223,35 +287,30 @@ public short readShort(int curBCI) { return (short) Bytes.beS2(code, curBCI + 1); } + /** + * Reads an unsigned byte for the current instruction (e.g. SIPUSH). The {@link Bytecodes#WIDE} + * modifier is NOT handled internally. + * + * @return the short value + */ public int opcode(int curBCI) { - if (curBCI < code.length) { - // opcode validity is performed at verification time. - return Bytes.beU1(code, curBCI); - } else { - return Bytecodes.END; - } + // opcode validity is performed at verification time. + return Bytes.beU1(code, curBCI); } private static int opcode(byte[] code, int curBCI) { - if (curBCI < code.length) { - // opcode validity is performed at verification time. - return Bytes.beU1(code, curBCI); - } else { - return Bytecodes.END; - } + // opcode validity is performed at verification time. + return Bytes.beU1(code, curBCI); } public int volatileOpcode(int curBCI) { - if (curBCI < code.length) { - // opcode validity is performed at verification time. - return Bytes.volatileBeU1(code, curBCI); - } else { - return Bytecodes.END; - } + // opcode validity is performed at verification time. + return Bytes.volatileBeU1(code, curBCI); } /** - * Gets the length of the current bytecode. + * Gets the length of the current bytecode. It takes into account bytecodes with non-constant + * size and the {@link Bytecodes#WIDE} bytecode. */ private int lengthOf(int curBCI) { int length = Bytecodes.lengthOf(opcode(curBCI)); @@ -265,9 +324,7 @@ private int lengthOf(int curBCI) { } case Bytecodes.WIDE: { int opc = Bytes.beU1(code, curBCI + 1); - if (opc == Bytecodes.RET) { - return 4; - } else if (opc == Bytecodes.IINC) { + if (opc == Bytecodes.IINC) { return 6; } else { return 4; // a load or store bytecode From 0ebf4dac99928b432657b05b3e05a39311312463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Tue, 11 May 2021 17:29:11 +0200 Subject: [PATCH 247/290] Handle WIDE modifier explicitly to avoid extra branches in the interpreter. --- .../truffle/espresso/nodes/BytecodeNode.java | 544 ++++++++++++------ 1 file changed, 378 insertions(+), 166 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 5af68ac93713..e43817d23e88 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -461,31 +461,37 @@ private void initArguments(Object[] arguments, long[] primitives, Object[] refs) } curSlot += JavaKind.Object.getSlotCount(); } + + Symbol[] methodSignature = getMethod().getParsedSignature(); + int argCount = Signatures.parameterCount(methodSignature, false); + CompilerAsserts.partialEvaluationConstant(argCount); for (int i = 0; i < argCount; ++i) { - JavaKind expectedkind = Signatures.parameterKind(getMethod().getParsedSignature(), i); - // @formatter:off - switch (expectedkind) { - case Boolean : setLocalInt(primitives, curSlot, ((boolean) arguments[i + receiverSlot]) ? 1 : 0); break; - case Byte : setLocalInt(primitives, curSlot, ((byte) arguments[i + receiverSlot])); break; - case Short : setLocalInt(primitives, curSlot, ((short) arguments[i + receiverSlot])); break; - case Char : setLocalInt(primitives, curSlot, ((char) arguments[i + receiverSlot])); break; - case Int : setLocalInt(primitives, curSlot, (int) arguments[i + receiverSlot]); break; - case Float : setLocalFloat(primitives, curSlot, (float) arguments[i + receiverSlot]); break; - case Long : setLocalLong(primitives, curSlot, (long) arguments[i + receiverSlot]); break; - case Double : setLocalDouble(primitives, curSlot, (double) arguments[i + receiverSlot]); break; - case Object : - setLocalObject(refs, curSlot, (StaticObject) arguments[i + receiverSlot]); - if (noForeignObjects.isValid() && ((StaticObject) arguments[i + receiverSlot]).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } - break; - default : - CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere("unexpected kind"); + Symbol argType = Signatures.parameterType(methodSignature, i); + if (argType.length() == 1) { + // @formatter:off + switch (argType.byteAt(0)) { + case 'Z' : setLocalInt(primitives, curSlot, ((boolean) arguments[i + receiverSlot]) ? 1 : 0); break; + case 'B' : setLocalInt(primitives, curSlot, ((byte) arguments[i + receiverSlot])); break; + case 'S' : setLocalInt(primitives, curSlot, ((short) arguments[i + receiverSlot])); break; + case 'C' : setLocalInt(primitives, curSlot, ((char) arguments[i + receiverSlot])); break; + case 'I' : setLocalInt(primitives, curSlot, (int) arguments[i + receiverSlot]); break; + case 'F' : setLocalFloat(primitives, curSlot, (float) arguments[i + receiverSlot]); break; + case 'J' : setLocalLong(primitives, curSlot, (long) arguments[i + receiverSlot]); ++curSlot; break; + case 'D' : setLocalDouble(primitives, curSlot, (double) arguments[i + receiverSlot]); ++curSlot; break; + default : + CompilerDirectives.transferToInterpreter(); + throw EspressoError.shouldNotReachHere("unexpected kind"); + } + // @formatter:on + } else { + // Reference type. + setLocalObject(refs, curSlot, (StaticObject) arguments[i + receiverSlot]); + if (noForeignObjects.isValid() && ((StaticObject) arguments[i + receiverSlot]).isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } } - // @formatter:on - curSlot += expectedkind.getSlotCount(); + ++curSlot; } } @@ -641,7 +647,7 @@ void initializeBody(VirtualFrame frame) { Object executeBody(VirtualFrame frame) { int curBCI = 0; int top = 0; - InstrumentationSupport instrument = this.instrumentation; + final InstrumentationSupport instrument = this.instrumentation; int statementIndex = -1; int nextStatementIndex = 0; @@ -678,17 +684,13 @@ Object executeBody(VirtualFrame frame) { CompilerDirectives.ensureVirtualized(primitives); CompilerDirectives.ensureVirtualized(refs); - if (Bytecodes.canTrap(curOpcode) || instrument != null) { - setBCI(frame, curBCI); - } - if (instrument != null) { + setBCI(frame, curBCI); instrument.notifyStatement(frame, statementIndex, nextStatementIndex); statementIndex = nextStatementIndex; } // @formatter:off - switchLabel: switch (curOpcode) { case NOP: break; case ACONST_NULL: putObject(refs, top, StaticObject.NULL); break; @@ -717,32 +719,65 @@ Object executeBody(VirtualFrame frame) { case LDC_W: // fall through case LDC2_W: putPoolConstant(primitives, refs, top, readCPI(curBCI), curOpcode); break; - case ILOAD: putInt(primitives, top, getLocalInt(primitives, bs.readLocalIndex(curBCI))); break; - case LLOAD: putLong(primitives, top, getLocalLong(primitives, bs.readLocalIndex(curBCI))); break; - case FLOAD: putFloat(primitives, top, getLocalFloat(primitives, bs.readLocalIndex(curBCI))); break; - case DLOAD: putDouble(primitives, top, getLocalDouble(primitives, bs.readLocalIndex(curBCI))); break; - case ALOAD: putObject(refs, top, getLocalObject(refs, bs.readLocalIndex(curBCI))); break; + case ILOAD: + putInt(primitives, top, getLocalInt(primitives, bs.readLocalIndex(curBCI))); + postLocalAccess(primitives, refs, curBCI); + break; + case LLOAD: + putLong(primitives, top, getLocalLong(primitives, bs.readLocalIndex(curBCI))); + postLocalAccess(primitives, refs, curBCI); + break; + case FLOAD: + putFloat(primitives, top, getLocalFloat(primitives, bs.readLocalIndex(curBCI))); + postLocalAccess(primitives, refs, curBCI); + break; + case DLOAD: + putDouble(primitives, top, getLocalDouble(primitives, bs.readLocalIndex(curBCI))); + postLocalAccess(primitives, refs, curBCI); + break; + case ALOAD: + putObject(refs, top, getLocalObject(refs, bs.readLocalIndex(curBCI))); + postLocalAccess(primitives, refs, curBCI); + break; case ILOAD_0: // fall through case ILOAD_1: // fall through case ILOAD_2: // fall through - case ILOAD_3: putInt(primitives, top, getLocalInt(primitives, curOpcode - ILOAD_0)); break; + case ILOAD_3: + putInt(primitives, top, getLocalInt(primitives, curOpcode - ILOAD_0)); + postLocalAccess(primitives, refs, curBCI); + break; case LLOAD_0: // fall through case LLOAD_1: // fall through case LLOAD_2: // fall through - case LLOAD_3: putLong(primitives, top, getLocalLong(primitives, curOpcode - LLOAD_0)); break; + case LLOAD_3: + putLong(primitives, top, getLocalLong(primitives, curOpcode - LLOAD_0)); + postLocalAccess(primitives, refs, curBCI); + break; case FLOAD_0: // fall through case FLOAD_1: // fall through case FLOAD_2: // fall through - case FLOAD_3: putFloat(primitives, top, getLocalFloat(primitives, curOpcode - FLOAD_0)); break; + case FLOAD_3: + putFloat(primitives, top, getLocalFloat(primitives, curOpcode - FLOAD_0)); + postLocalAccess(primitives, refs, curBCI); + break; case DLOAD_0: // fall through case DLOAD_1: // fall through case DLOAD_2: // fall through - case DLOAD_3: putDouble(primitives, top, getLocalDouble(primitives, curOpcode - DLOAD_0)); break; - case ALOAD_0: putObject(refs, top, getLocalObject(refs, 0)); break; + case DLOAD_3: + putDouble(primitives, top, getLocalDouble(primitives, curOpcode - DLOAD_0)); + postLocalAccess(primitives, refs, curBCI); + break; + case ALOAD_0: + putObject(refs, top, getLocalObject(refs, 0)); + postLocalAccess(primitives, refs, curBCI); + break; case ALOAD_1: // fall through case ALOAD_2: // fall through - case ALOAD_3: putObject(refs, top, getLocalObject(refs, curOpcode - ALOAD_0)); break; + case ALOAD_3: + putObject(refs, top, getLocalObject(refs, curOpcode - ALOAD_0)); + postLocalAccess(primitives, refs, curBCI); + break; case IALOAD: // fall through case LALOAD: // fall through @@ -752,39 +787,69 @@ Object executeBody(VirtualFrame frame) { case CALOAD: // fall through case SALOAD: arrayLoad(frame, primitives, refs, top, curBCI, curOpcode); break; case AALOAD: - arrayLoad(frame, primitives, refs, top, curBCI, curOpcode); + arrayLoad(frame, primitives, refs, top, curBCI, AALOAD); if (noForeignObjects.isValid() && peekObject(refs, top - 2).isForeignObject()) { CompilerDirectives.transferToInterpreterAndInvalidate(); noForeignObjects.invalidate(); } break; - case ISTORE: setLocalInt(primitives, bs.readLocalIndex(curBCI), popInt(primitives, top - 1)); break; - case LSTORE: setLocalLong(primitives, bs.readLocalIndex(curBCI), popLong(primitives, top - 1)); break; - case FSTORE: setLocalFloat(primitives, bs.readLocalIndex(curBCI), popFloat(primitives, top - 1)); break; - case DSTORE: setLocalDouble(primitives, bs.readLocalIndex(curBCI), popDouble(primitives, top - 1)); break; - case ASTORE: setLocalObjectOrReturnAddress(refs, bs.readLocalIndex(curBCI), popReturnAddressOrObject(refs, top - 1)); break; + case ISTORE: + setLocalInt(primitives, bs.readLocalIndex(curBCI), popInt(primitives, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; + case LSTORE: + setLocalLong(primitives, bs.readLocalIndex(curBCI), popLong(primitives, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; + case FSTORE: + setLocalFloat(primitives, bs.readLocalIndex(curBCI), popFloat(primitives, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; + case DSTORE: + setLocalDouble(primitives, bs.readLocalIndex(curBCI), popDouble(primitives, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; + case ASTORE: + setLocalObjectOrReturnAddress(refs, bs.readLocalIndex(curBCI), popReturnAddressOrObject(refs, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; case ISTORE_0: // fall through case ISTORE_1: // fall through case ISTORE_2: // fall through - case ISTORE_3: setLocalInt(primitives, curOpcode - ISTORE_0, popInt(primitives, top - 1)); break; + case ISTORE_3: + setLocalInt(primitives, curOpcode - ISTORE_0, popInt(primitives, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; case LSTORE_0: // fall through case LSTORE_1: // fall through case LSTORE_2: // fall through - case LSTORE_3: setLocalLong(primitives, curOpcode - LSTORE_0, popLong(primitives, top - 1)); break; + case LSTORE_3: + setLocalLong(primitives, curOpcode - LSTORE_0, popLong(primitives, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; case FSTORE_0: // fall through case FSTORE_1: // fall through case FSTORE_2: // fall through - case FSTORE_3: setLocalFloat(primitives, curOpcode - FSTORE_0, popFloat(primitives, top - 1)); break; + case FSTORE_3: + setLocalFloat(primitives, curOpcode - FSTORE_0, popFloat(primitives, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; case DSTORE_0: // fall through case DSTORE_1: // fall through case DSTORE_2: // fall through - case DSTORE_3: setLocalDouble(primitives, curOpcode - DSTORE_0, popDouble(primitives, top - 1)); break; + case DSTORE_3: + setLocalDouble(primitives, curOpcode - DSTORE_0, popDouble(primitives, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; case ASTORE_0: // fall through case ASTORE_1: // fall through case ASTORE_2: // fall through - case ASTORE_3: setLocalObjectOrReturnAddress(refs, curOpcode - ASTORE_0, popReturnAddressOrObject(refs, top - 1)); break; + case ASTORE_3: + setLocalObjectOrReturnAddress(refs, curOpcode - ASTORE_0, popReturnAddressOrObject(refs, top - 1)); + postLocalAccess(primitives, refs, curBCI); + break; case IASTORE: // fall through case LASTORE: // fall through @@ -817,10 +882,10 @@ Object executeBody(VirtualFrame frame) { case FADD: putFloat(primitives, top - 2, popFloat(primitives, top - 1) + popFloat(primitives, top - 2)); break; case DADD: putDouble(primitives, top - 4, popDouble(primitives, top - 1) + popDouble(primitives, top - 3)); break; - case ISUB: putInt(primitives, top - 2, -popInt(primitives, top - 1) + popInt(primitives, top - 2)); break; - case LSUB: putLong(primitives, top - 4, -popLong(primitives, top - 1) + popLong(primitives, top - 3)); break; - case FSUB: putFloat(primitives, top - 2, -popFloat(primitives, top - 1) + popFloat(primitives, top - 2)); break; - case DSUB: putDouble(primitives, top - 4, -popDouble(primitives, top - 1) + popDouble(primitives, top - 3)); break; + case ISUB: putInt(primitives, top - 2, popInt(primitives, top - 2) - popInt(primitives, top - 1)); break; + case LSUB: putLong(primitives, top - 4, popLong(primitives, top - 3) - popLong(primitives, top - 1)); break; + case FSUB: putFloat(primitives, top - 2, popFloat(primitives, top - 2) - popFloat(primitives, top - 1)); break; + case DSUB: putDouble(primitives, top - 4, popDouble(primitives, top - 3) - popDouble(primitives, top - 1)); break; case IMUL: putInt(primitives, top - 2, popInt(primitives, top - 1) * popInt(primitives, top - 2)); break; case LMUL: putLong(primitives, top - 4, popLong(primitives, top - 1) * popLong(primitives, top - 3)); break; @@ -858,7 +923,10 @@ Object executeBody(VirtualFrame frame) { case IXOR: putInt(primitives, top - 2, popInt(primitives, top - 1) ^ popInt(primitives, top - 2)); break; case LXOR: putLong(primitives, top - 4, popLong(primitives, top - 1) ^ popLong(primitives, top - 3)); break; - case IINC: setLocalInt(primitives, bs.readLocalIndex(curBCI), getLocalInt(primitives, bs.readLocalIndex(curBCI)) + bs.readIncrement(curBCI)); break; + case IINC: + setLocalInt(primitives, bs.readLocalIndex1(curBCI), getLocalInt(primitives, bs.readLocalIndex1(curBCI)) + bs.readIncrement1(curBCI)); + postLocalAccess(primitives, refs, curBCI); + break; case I2L: putLong(primitives, top - 1, popInt(primitives, top - 1)); break; case I2F: putFloat(primitives, top - 1, popInt(primitives, top - 1)); break; @@ -892,43 +960,81 @@ Object executeBody(VirtualFrame frame) { case IFGE: // fall through case IFGT: // fall through case IFLE: // fall through + if (takeBranchPrimitive1(popInt(primitives, top - 1), curOpcode)) { + int targetBCI = bs.readBranchDest2(curBCI); + nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); + top += Bytecodes.stackEffectOf(IFLE); + curBCI = targetBCI; + continue loop; + } + break; + case IF_ICMPEQ: // fall through case IF_ICMPNE: // fall through case IF_ICMPLT: // fall through case IF_ICMPGE: // fall through case IF_ICMPGT: // fall through - case IF_ICMPLE: // fall through + case IF_ICMPLE: + if (takeBranchPrimitive2(popInt(primitives, top - 1), popInt(primitives, top - 2), curOpcode)) { + nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, bs.readBranchDest2(curBCI), statementIndex, instrument, loopCount); + top += Bytecodes.stackEffectOf(IF_ICMPLE); + curBCI = bs.readBranchDest2(curBCI); + continue loop; + } + break; + case IF_ACMPEQ: // fall through - case IF_ACMPNE: // fall through + case IF_ACMPNE: + if (takeBranchRef2(popObject(refs, top - 1), popObject(refs, top - 2), curOpcode)) { + int targetBCI = bs.readBranchDest2(curBCI); + nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); + top += Bytecodes.stackEffectOf(IF_ACMPNE); + curBCI = targetBCI; + continue loop; + } + break; - // TODO(peterssen): Order shuffled. - case GOTO: // fall through - case GOTO_W: // fall through case IFNULL: // fall through - - // @formatter:on case IFNONNULL: - - if (takeBranch(primitives, refs, top, curOpcode)) { - int targetBCI = bs.readBranchDest(curBCI); + if (takeBranchRef1(popObject(refs, top - 1), curOpcode)) { + int targetBCI = bs.readBranchDest2(curBCI); nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(IFNONNULL); curBCI = targetBCI; continue loop; } - break switchLabel; + break; - case JSR: // fall through + case GOTO: { + int targetBCI = bs.readBranchDest2(curBCI); + nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); + curBCI = targetBCI; + continue loop; + } + case GOTO_W: { + int targetBCI = bs.readBranchDest4(curBCI); + nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); + curBCI = targetBCI; + continue loop; + } + case JSR: { + putReturnAddress(refs, top, bs.nextBCI(curBCI)); + int targetBCI = bs.readBranchDest2(curBCI); + nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); + top += Bytecodes.stackEffectOf(JSR); + curBCI = targetBCI; + continue loop; + } case JSR_W: { putReturnAddress(refs, top, bs.nextBCI(curBCI)); - int targetBCI = bs.readBranchDest(curBCI); + int targetBCI = bs.readBranchDest4(curBCI); nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(JSR_W); curBCI = targetBCI; continue loop; } case RET: { - int targetBCI = getLocalReturnAddress(refs, bs.readLocalIndex(curBCI)); + int targetBCI = getLocalReturnAddress(refs, bs.readLocalIndex1(curBCI)); postLocalAccess(primitives, refs, curBCI); if (jsrBci == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -943,7 +1049,7 @@ Object executeBody(VirtualFrame frame) { CompilerAsserts.partialEvaluationConstant(jsr); targetBCI = jsr; nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(RET); curBCI = targetBCI; continue loop; } @@ -952,7 +1058,7 @@ Object executeBody(VirtualFrame frame) { jsrBci[curBCI] = Arrays.copyOf(jsrBci[curBCI], jsrBci[curBCI].length + 1); jsrBci[curBCI][jsrBci[curBCI].length - 1] = targetBCI; nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(RET); curBCI = targetBCI; continue loop; } @@ -973,7 +1079,7 @@ Object executeBody(VirtualFrame frame) { targetBCI = switchHelper.defaultTarget(bs, curBCI); } nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(TABLESWITCH); curBCI = targetBCI; continue loop; } @@ -985,7 +1091,7 @@ Object executeBody(VirtualFrame frame) { // Key found. int targetBCI = switchHelper.targetAt(bs, curBCI, i - low); nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(TABLESWITCH); curBCI = targetBCI; continue loop; } @@ -994,7 +1100,7 @@ Object executeBody(VirtualFrame frame) { // Key not found. int targetBCI = switchHelper.defaultTarget(bs, curBCI); nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(TABLESWITCH); curBCI = targetBCI; continue loop; } @@ -1014,7 +1120,7 @@ Object executeBody(VirtualFrame frame) { // Key found. int targetBCI = curBCI + switchHelper.offsetAt(bs, curBCI, mid); nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(LOOKUPSWITCH); curBCI = targetBCI; continue loop; } @@ -1023,7 +1129,7 @@ Object executeBody(VirtualFrame frame) { // Key not found. int targetBCI = switchHelper.defaultTarget(bs, curBCI); nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); - top += Bytecodes.stackEffectOf(curOpcode); + top += Bytecodes.stackEffectOf(LOOKUPSWITCH); curBCI = targetBCI; continue loop; } @@ -1047,34 +1153,96 @@ Object executeBody(VirtualFrame frame) { case INVOKEVIRTUAL: // fall through case INVOKESPECIAL: // fall through - case INVOKESTATIC: // fall through + case INVOKESTATIC: // fall through + case INVOKEINTERFACE: + setBCI(frame, curBCI); + top += quickenInvoke(frame, primitives, refs, top, curBCI, curOpcode, statementIndex); break; - case INVOKEINTERFACE: top += quickenInvoke(frame, primitives, refs, top, curBCI, curOpcode, statementIndex); break; + case NEW : putObject(refs, top, InterpreterToVM.newObject(resolveType(NEW, readCPI(curBCI)), true)); break; + case NEWARRAY : putObject(refs, top - 1, InterpreterToVM.allocatePrimitiveArray(bs.readByte(curBCI), popInt(primitives, top - 1), getMeta(), this)); break; + case ANEWARRAY : putObject(refs, top - 1, InterpreterToVM.newReferenceArray(resolveType(ANEWARRAY, readCPI(curBCI)), popInt(primitives, top - 1), this)); break; - case NEW : putObject(refs, top, InterpreterToVM.newObject(resolveType(curOpcode, readCPI(curBCI)), true)); break; - case NEWARRAY : putObject(refs, top - 1, InterpreterToVM.allocatePrimitiveArray(bs.readByte(curBCI), popInt(primitives, top - 1), getMeta())); break; - case ANEWARRAY : putObject(refs, top - 1, allocateArray(resolveType(curOpcode, readCPI(curBCI)), popInt(primitives, top - 1))); break; case ARRAYLENGTH : arrayLength(frame, primitives, refs, top, curBCI); break; - case ATHROW : throw getMeta().throwException(nullCheck(popObject(refs, top - 1))); - case CHECKCAST : top += quickenCheckCast(frame, primitives, refs, top, curBCI, curOpcode); break; - case INSTANCEOF : top += quickenInstanceOf(frame, primitives, refs, top, curBCI, curOpcode); break; + case ATHROW : + setBCI(frame, curBCI); + throw getMeta().throwException(nullCheck(popObject(refs, top - 1))); + + case CHECKCAST : top += quickenCheckCast(frame, primitives, refs, top, curBCI, CHECKCAST); break; + case INSTANCEOF : top += quickenInstanceOf(frame, primitives, refs, top, curBCI, INSTANCEOF); break; case MONITORENTER: getRoot().monitorEnter(frame, nullCheck(popObject(refs, top - 1))); break; case MONITOREXIT : getRoot().monitorExit(frame, nullCheck(popObject(refs, top - 1))); break; - case WIDE: - CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere("BytecodeStream.currentBC() should never return this bytecode."); + case WIDE: { + int wideOpcode = bs.opcode(curBCI + 1); + switch (wideOpcode) { + case ILOAD: putInt(primitives, top, getLocalInt(primitives, bs.readLocalIndex2(curBCI))); break; + case LLOAD: putLong(primitives, top, getLocalLong(primitives, bs.readLocalIndex2(curBCI))); break; + case FLOAD: putFloat(primitives, top, getLocalFloat(primitives, bs.readLocalIndex2(curBCI))); break; + case DLOAD: putDouble(primitives, top, getLocalDouble(primitives, bs.readLocalIndex2(curBCI))); break; + case ALOAD: putObject(refs, top, getLocalObject(refs, bs.readLocalIndex2(curBCI))); break; + + case ISTORE: setLocalInt(primitives, bs.readLocalIndex2(curBCI), popInt(primitives, top - 1)); break; + case LSTORE: setLocalLong(primitives, bs.readLocalIndex2(curBCI), popLong(primitives, top - 1)); break; + case FSTORE: setLocalFloat(primitives, bs.readLocalIndex2(curBCI), popFloat(primitives, top - 1)); break; + case DSTORE: setLocalDouble(primitives, bs.readLocalIndex2(curBCI), popDouble(primitives, top - 1)); break; + case ASTORE: setLocalObjectOrReturnAddress(refs, bs.readLocalIndex2(curBCI), popReturnAddressOrObject(refs, top - 1)); break; + case IINC: setLocalInt(primitives, bs.readLocalIndex2(curBCI), getLocalInt(primitives, bs.readLocalIndex2(curBCI)) + bs.readIncrement2(curBCI)); break; + case RET: { + int targetBCI = getLocalReturnAddress(refs, bs.readLocalIndex2(curBCI)); + postLocalAccess(primitives, refs, curBCI); + if (jsrBci == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + jsrBci = new int[bs.endBCI()][]; + } + if (jsrBci[curBCI] == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + jsrBci[curBCI] = new int[]{targetBCI}; + } + for (int jsr : jsrBci[curBCI]) { + if (jsr == targetBCI) { + CompilerAsserts.partialEvaluationConstant(jsr); + targetBCI = jsr; + nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); + top += Bytecodes.stackEffectOf(RET); + curBCI = targetBCI; + continue loop; + } + } + CompilerDirectives.transferToInterpreterAndInvalidate(); + jsrBci[curBCI] = Arrays.copyOf(jsrBci[curBCI], jsrBci[curBCI].length + 1); + jsrBci[curBCI][jsrBci[curBCI].length - 1] = targetBCI; + nextStatementIndex = beforeJumpChecks(primitives, refs, curBCI, targetBCI, statementIndex, instrument, loopCount); + top += Bytecodes.stackEffectOf(RET); + curBCI = targetBCI; + continue loop; + } + default: + CompilerDirectives.transferToInterpreter(); + throw EspressoError.shouldNotReachHere(Bytecodes.nameOf(curOpcode)); + } + postLocalAccess(primitives, refs, curBCI); + int targetBCI = bs.nextBCI(curBCI); + edgeLocalAnalysis(primitives, refs, curBCI, targetBCI); + top += Bytecodes.stackEffectOf(wideOpcode); + curBCI += targetBCI; + continue loop; + } - case MULTIANEWARRAY: top += allocateMultiArray(primitives, refs, top, resolveType(curOpcode, readCPI(curBCI)), bs.readUByte(curBCI + 3)); break; + case MULTIANEWARRAY: top += allocateMultiArray(primitives, refs, top, resolveType(MULTIANEWARRAY, readCPI(curBCI)), bs.readUByte(curBCI + 3)); break; case BREAKPOINT: CompilerDirectives.transferToInterpreter(); throw EspressoError.unimplemented(Bytecodes.nameOf(curOpcode) + " not supported."); - case INVOKEDYNAMIC: top += quickenInvokeDynamic(frame, primitives, refs, top, curBCI, curOpcode); break; + case INVOKEDYNAMIC: + setBCI(frame, curBCI); + top += quickenInvokeDynamic(frame, primitives, refs, top, curBCI, INVOKEDYNAMIC); + break; + case QUICK: { + setBCI(frame, curBCI); // Force a volatile read of the opcode. if (bs.currentVolatileBC(curBCI) != QUICK) { // Possible case of read reordering. Retry handling the bytecode to make sure we get a correct CPI. @@ -1106,7 +1274,10 @@ Object executeBody(VirtualFrame frame) { } break; } - case SLIM_QUICK: top += sparseNodes[curBCI].execute(frame, primitives, refs); break; + case SLIM_QUICK: + setBCI(frame, curBCI); + top += sparseNodes[curBCI].execute(frame, primitives, refs); + break; default: CompilerDirectives.transferToInterpreter(); @@ -1114,6 +1285,7 @@ Object executeBody(VirtualFrame frame) { } // @formatter:on } catch (EspressoException | AbstractTruffleException | StackOverflowError | OutOfMemoryError e) { + setBCI(frame, curBCI); if (instrument != null && e instanceof EspressoException) { instrument.notifyExceptionAt(frame, e, statementIndex); } @@ -1208,33 +1380,36 @@ Object executeBody(VirtualFrame frame) { } } catch (EspressoExitException e) { CompilerDirectives.transferToInterpreter(); + setBCI(frame, curBCI); getRoot().abortMonitor(frame); // Tearing down the VM, no need to report loop count. throw e; } // This check includes newly rewritten QUICK nodes, not just curOpcode == quick - if (noForeignObjects.isValid() && (bs.currentBC(curBCI) == QUICK || bs.currentBC(curBCI) == SLIM_QUICK)) { - BaseQuickNode quickNode; - if (bs.currentBC(curBCI) == QUICK) { - quickNode = nodes[readCPI(curBCI)]; - } else { - assert bs.currentBC(curBCI) == SLIM_QUICK; - quickNode = sparseNodes[curBCI]; - } - if (quickNode.producedForeignObject(refs)) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); + if (noForeignObjects.isValid()) { + int opcode = bs.opcode(curBCI); + if (opcode == QUICK || opcode == SLIM_QUICK) { + BaseQuickNode quickNode; + if (opcode == QUICK) { + quickNode = nodes[readCPI(curBCI)]; + } else { + assert opcode == SLIM_QUICK; + quickNode = sparseNodes[curBCI]; + } + if (quickNode.producedForeignObject(refs)) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } } } - if (Bytecodes.isLoad(curOpcode) || Bytecodes.isStore(curOpcode)) { - postLocalAccess(primitives, refs, curBCI); - } - top += Bytecodes.stackEffectOf(curOpcode); - int targetBCI = bs.nextBCI(curBCI); + assert curOpcode != WIDE && curOpcode != LOOKUPSWITCH && curOpcode != TABLESWITCH; + + int targetBCI = curBCI + Bytecodes.lengthOf(curOpcode); + edgeLocalAnalysis(primitives, refs, curBCI, targetBCI); if (instrument != null) { nextStatementIndex = instrument.getNextStatementIndex(statementIndex, targetBCI); } - edgeLocalAnalysis(primitives, refs, curBCI, targetBCI); + top += Bytecodes.stackEffectOf(curOpcode); curBCI = targetBCI; } } @@ -1309,31 +1484,62 @@ public InstrumentableNode materializeInstrumentableNodes(Set= 0; + case IFGT : return operand > 0; + case IFLE : return operand <= 0; + default : + CompilerDirectives.transferToInterpreter(); + throw EspressoError.shouldNotReachHere("expecting IFEQ,IFNE,IFLT,IFGE,IFGT,IFLE"); + } + // @formatter:on + } + + private static boolean takeBranchPrimitive2(int operand1, int operand2, int opcode) { + assert IF_ICMPEQ <= opcode && opcode <= IF_ICMPLE; // @formatter:off switch (opcode) { - case IFEQ : return popInt(primitives, top - 1) == 0; - case IFNE : return popInt(primitives, top - 1) != 0; - case IFLT : return popInt(primitives, top - 1) < 0; - case IFGE : return popInt(primitives, top - 1) >= 0; - case IFGT : return popInt(primitives, top - 1) > 0; - case IFLE : return popInt(primitives, top - 1) <= 0; - case IF_ICMPEQ : return popInt(primitives, top - 1) == popInt(primitives, top - 2); - case IF_ICMPNE : return popInt(primitives, top - 1) != popInt(primitives, top - 2); - case IF_ICMPLT : return popInt(primitives, top - 1) > popInt(primitives, top - 2); - case IF_ICMPGE : return popInt(primitives, top - 1) <= popInt(primitives, top - 2); - case IF_ICMPGT : return popInt(primitives, top - 1) < popInt(primitives, top - 2); - case IF_ICMPLE : return popInt(primitives, top - 1) >= popInt(primitives, top - 2); - case IF_ACMPEQ : return popObject(refs, top - 1) == popObject(refs, top - 2); - case IF_ACMPNE : return popObject(refs, top - 1) != popObject(refs, top - 2); - case GOTO : // fall though - case GOTO_W : return true; // unconditional - case IFNULL : return StaticObject.isNull(popObject(refs, top - 1)); - case IFNONNULL : return StaticObject.notNull(popObject(refs, top - 1)); + case IF_ICMPEQ : return operand1 == operand2; + case IF_ICMPNE : return operand1 != operand2; + case IF_ICMPLT : return operand1 > operand2; + case IF_ICMPGE : return operand1 <= operand2; + case IF_ICMPGT : return operand1 < operand2; + case IF_ICMPLE : return operand1 >= operand2; default : CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere("non-branching bytecode"); + throw EspressoError.shouldNotReachHere("expecting IF_ICMPEQ,IF_ICMPNE,IF_ICMPLT,IF_ICMPGE,IF_ICMPGT,IF_ICMPLE"); + } + // @formatter:on + } + + private static boolean takeBranchRef2(StaticObject operand1, StaticObject operand2, int opcode) { + assert IF_ACMPEQ <= opcode && opcode <= IF_ACMPNE; + // @formatter:off + switch (opcode) { + case IF_ACMPEQ : return operand1 == operand2; + case IF_ACMPNE : return operand1 != operand2; + default : + CompilerDirectives.transferToInterpreter(); + throw EspressoError.shouldNotReachHere("expecting IF_ACMPEQ,IF_ACMPNE"); } // @formatter:on } @@ -2350,24 +2556,28 @@ public static Object[] popArguments(long[] primitives, Object[] refs, int top, b int argAt = top - 1; for (int i = argCount - 1; i >= 0; --i) { - JavaKind kind = Signatures.parameterKind(signature, i); - // @formatter:off - switch (kind) { - case Boolean : args[i + extraParam] = (popInt(primitives, argAt) != 0); break; - case Byte : args[i + extraParam] = (byte) popInt(primitives, argAt); break; - case Short : args[i + extraParam] = (short) popInt(primitives, argAt); break; - case Char : args[i + extraParam] = (char) popInt(primitives, argAt); break; - case Int : args[i + extraParam] = popInt(primitives, argAt); break; - case Float : args[i + extraParam] = popFloat(primitives, argAt); break; - case Long : args[i + extraParam] = popLong(primitives, argAt); break; - case Double : args[i + extraParam] = popDouble(primitives, argAt); break; - case Object : args[i + extraParam] = popObject(refs, argAt); break; - default : - CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere(); + Symbol argType = Signatures.parameterType(signature, i); + if (argType.length() == 1) { + // @formatter:off + switch (argType.byteAt(0)) { + case 'Z' : args[i + extraParam] = (popInt(primitives, argAt) != 0); break; + case 'B' : args[i + extraParam] = (byte) popInt(primitives, argAt); break; + case 'S' : args[i + extraParam] = (short) popInt(primitives, argAt); break; + case 'C' : args[i + extraParam] = (char) popInt(primitives, argAt); break; + case 'I' : args[i + extraParam] = popInt(primitives, argAt); break; + case 'F' : args[i + extraParam] = popFloat(primitives, argAt); break; + case 'J' : args[i + extraParam] = popLong(primitives, argAt); --argAt; break; + case 'D' : args[i + extraParam] = popDouble(primitives, argAt); --argAt; break; + default : + CompilerDirectives.transferToInterpreter(); + throw EspressoError.shouldNotReachHere(); + } + // @formatter:on + } else { + args[i + extraParam] = popObject(refs, argAt); } - // @formatter:on - argAt -= kind.getSlotCount(); + --argAt; + } if (hasReceiver) { args[0] = popObject(refs, argAt); @@ -2381,27 +2591,29 @@ public static Object[] popBasicArgumentsWithArray(long[] primitives, Object[] re // Use basic types CompilerAsserts.partialEvaluationConstant(argCount); CompilerAsserts.partialEvaluationConstant(signature); - int argAt = top - 1; for (int i = argCount - 1; i >= 0; --i) { - JavaKind kind = Signatures.parameterKind(signature, i); - // @formatter:off - switch (kind) { - case Boolean : // Fall through - case Byte : // Fall through - case Short : // Fall through - case Char : // Fall through - case Int : args[i + start] = popInt(primitives, argAt); break; - case Float : args[i + start] = popFloat(primitives, argAt); break; - case Long : args[i + start] = popLong(primitives, argAt); break; - case Double : args[i + start] = popDouble(primitives, argAt); break; - case Object : args[i + start] = popObject(refs, argAt); break; - default : - CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere(); + Symbol argType = Signatures.parameterType(signature, i); + if (argType.length() == 1) { + // @formatter:off + switch (argType.byteAt(0)) { + case 'Z' : // fall through + case 'B' : // fall through + case 'S' : // fall through + case 'C' : // fall through + case 'I' : args[i + start] = popInt(primitives, argAt); break; + case 'F' : args[i + start] = popFloat(primitives, argAt); break; + case 'J' : args[i + start] = popLong(primitives, argAt); --argAt; break; + case 'D' : args[i + start] = popDouble(primitives, argAt); --argAt; break; + default : + CompilerDirectives.transferToInterpreter(); + throw EspressoError.shouldNotReachHere(); + } + // @formatter:on + } else { + args[i + start] = popObject(refs, argAt); } - // @formatter:on - argAt -= kind.getSlotCount(); + --argAt; } return args; } From 9a2970daaf15957bcb9540ba4dc42ae28c996406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Tue, 11 May 2021 17:30:20 +0200 Subject: [PATCH 248/290] Profile negative array length exceptions. --- .../truffle/espresso/nodes/BytecodeNode.java | 9 --------- .../truffle/espresso/vm/InterpreterToVM.java | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index e43817d23e88..ceef1c9712ff 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -441,10 +441,6 @@ public SourceSection getSourceSectionAtBCI(int bci) { @ExplodeLoop private void initArguments(Object[] arguments, long[] primitives, Object[] refs) { - - int argCount = Signatures.parameterCount(getMethod().getParsedSignature(), false); - - CompilerAsserts.partialEvaluationConstant(argCount); CompilerAsserts.partialEvaluationConstant(primitives.length); CompilerAsserts.partialEvaluationConstant(refs.length); @@ -2122,11 +2118,6 @@ private Field resolveField(int opcode, char cpi) { // region Instance/array allocation - private static StaticObject allocateArray(Klass componentType, int length) { - assert !componentType.isPrimitive(); - return InterpreterToVM.newReferenceArray(componentType, length); - } - @ExplodeLoop private int allocateMultiArray(long[] primitives, Object[] refs, int top, Klass klass, int allocatedDimensions) { assert klass.isArray(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java index 5539da126198..f28e228d17ff 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java @@ -462,7 +462,7 @@ public static void setFieldObject(StaticObject value, StaticObject obj, Field fi field.setObject(obj, value); } - public static StaticObject newReferenceArray(Klass componentType, int length) { + public static StaticObject newReferenceArray(Klass componentType, int length, BytecodeNode bytecodeNode) { if (length < 0) { // componentType is not always PE constant e.g. when called from the Array#newInstance // substitution. The derived context and meta accessor are not PE constant @@ -472,6 +472,9 @@ public static StaticObject newReferenceArray(Klass componentType, int length) { // The definitive solution would be to distinguish the cases where the exception klass // is PE constant from the cases where it's dynamic. We can further reduce the dynamic // cases with an inline cache in the above substitution. + if (bytecodeNode != null) { + bytecodeNode.enterImplicitExceptionProfile(); + } throw throwNegativeArraySizeException(componentType.getMeta()); } assert length >= 0; @@ -480,6 +483,11 @@ public static StaticObject newReferenceArray(Klass componentType, int length) { return StaticObject.createArray(componentType.getArrayClass(), arr); } + public static StaticObject newReferenceArray(Klass componentType, int length) { + assert !componentType.isPrimitive(); + return newReferenceArray(componentType, length, null); + } + @TruffleBoundary(transferToInterpreterOnException = false) private static EspressoException throwNegativeArraySizeException(Meta meta) { throw meta.throwException(meta.java_lang_NegativeArraySizeException); @@ -524,8 +532,15 @@ public StaticObject apply(int i) { } public static StaticObject allocatePrimitiveArray(byte jvmPrimitiveType, int length, Meta meta) { + return allocatePrimitiveArray(jvmPrimitiveType, length, meta, null); + } + + public static StaticObject allocatePrimitiveArray(byte jvmPrimitiveType, int length, Meta meta, BytecodeNode bytecodeNode) { // the constants for the cpi are loosely defined and no real cpi indices. if (length < 0) { + if (bytecodeNode != null) { + bytecodeNode.enterImplicitExceptionProfile(); + } throw meta.throwException(meta.java_lang_NegativeArraySizeException); } // @formatter:off From 375dce1a8c33a4e674cf6d15cb5733f03988fcce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Tue, 11 May 2021 17:33:54 +0200 Subject: [PATCH 249/290] Increase loop reporting stride to 256. --- .../src/com/oracle/truffle/espresso/nodes/BytecodeNode.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index ceef1c9712ff..bcf86f4b28a8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -360,7 +360,7 @@ public final class BytecodeNode extends EspressoMethodNode { private static final DebugCounter QUICKENED_INVOKES = DebugCounter.create("Quickened invokes (excluding INDY)"); private static final DebugCounter[] BYTECODE_HISTOGRAM; - private static final int REPORT_LOOP_STRIDE = 1 << 6; + private static final int REPORT_LOOP_STRIDE = 1 << 8; static { BYTECODE_HISTOGRAM = new DebugCounter[0xFF]; @@ -1633,9 +1633,9 @@ private int beforeJumpChecks(long[] primitives, Object[] refs, int curBCI, int t int nextStatementIndex = 0; if (targetBCI <= curBCI) { checkStopping(); - if ((++loopCount[0] & (REPORT_LOOP_STRIDE - 1)) == 0) { + if (++loopCount[0] >= REPORT_LOOP_STRIDE) { LoopNode.reportLoopCount(this, REPORT_LOOP_STRIDE); - loopCount[0] -= REPORT_LOOP_STRIDE; + loopCount[0] = 0; } } if (instrument != null) { From fa2351f95b44e49575deba6262a1903ecd39d176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Tue, 11 May 2021 22:50:36 +0200 Subject: [PATCH 250/290] Move noForeignObject assumption invalidation out of the common tail, to usages. --- .../truffle/espresso/nodes/BytecodeNode.java | 21 ++++--------------- .../nodes/helper/AbstractGetFieldNode.java | 10 ++++++++- .../espresso/nodes/quick/BaseQuickNode.java | 2 -- .../espresso/nodes/quick/CheckCastNode.java | 5 ----- .../espresso/nodes/quick/InstanceOfNode.java | 5 ----- .../nodes/quick/interop/ArrayLengthNode.java | 5 ----- .../quick/interop/ByteArrayLoadNode.java | 5 ----- .../quick/interop/ByteArrayStoreNode.java | 5 ----- .../quick/interop/CharArrayLoadNode.java | 5 ----- .../quick/interop/CharArrayStoreNode.java | 5 ----- .../quick/interop/DoubleArrayLoadNode.java | 5 ----- .../quick/interop/DoubleArrayStoreNode.java | 5 ----- .../quick/interop/FloatArrayLoadNode.java | 5 ----- .../quick/interop/FloatArrayStoreNode.java | 5 ----- .../nodes/quick/interop/IntArrayLoadNode.java | 5 ----- .../quick/interop/IntArrayStoreNode.java | 5 ----- .../quick/interop/LongArrayLoadNode.java | 5 ----- .../quick/interop/LongArrayStoreNode.java | 5 ----- .../quick/interop/QuickenedGetFieldNode.java | 7 ------- .../quick/interop/QuickenedPutFieldNode.java | 5 ----- .../quick/interop/ReferenceArrayLoadNode.java | 15 +++++++------ .../interop/ReferenceArrayStoreNode.java | 5 ----- .../quick/interop/ShortArrayLoadNode.java | 5 ----- .../quick/interop/ShortArrayStoreNode.java | 5 ----- .../nodes/quick/invoke/InlinedGetterNode.java | 5 ----- .../nodes/quick/invoke/InlinedSetterNode.java | 5 ----- .../invoke/InvokeDynamicCallSiteNode.java | 16 +++++++++----- .../nodes/quick/invoke/InvokeHandleNode.java | 18 +++++++++++----- .../quick/invoke/InvokeInterfaceNode.java | 16 +++++++++----- .../nodes/quick/invoke/InvokeSpecialNode.java | 17 ++++++++++----- .../nodes/quick/invoke/InvokeStaticNode.java | 19 ++++++++++++----- .../nodes/quick/invoke/InvokeVirtualNode.java | 18 +++++++++++----- 32 files changed, 96 insertions(+), 168 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index bcf86f4b28a8..28dca28ba570 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -396,6 +396,10 @@ public final class BytecodeNode extends EspressoMethodNode { @Child private volatile InstrumentationSupport instrumentation; + public Assumption getNoForeignObjects() { + return noForeignObjects; + } + private final Assumption noForeignObjects; // Cheap profile for implicit exceptions e.g. null checks, division by 0, index out of bounds. @@ -1381,23 +1385,6 @@ Object executeBody(VirtualFrame frame) { // Tearing down the VM, no need to report loop count. throw e; } - // This check includes newly rewritten QUICK nodes, not just curOpcode == quick - if (noForeignObjects.isValid()) { - int opcode = bs.opcode(curBCI); - if (opcode == QUICK || opcode == SLIM_QUICK) { - BaseQuickNode quickNode; - if (opcode == QUICK) { - quickNode = nodes[readCPI(curBCI)]; - } else { - assert opcode == SLIM_QUICK; - quickNode = sparseNodes[curBCI]; - } - if (quickNode.producedForeignObject(refs)) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } - } - } assert curOpcode != WIDE && curOpcode != LOOKUPSWITCH && curOpcode != TABLESWITCH; int targetBCI = curBCI + Bytecodes.lengthOf(curOpcode); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/AbstractGetFieldNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/AbstractGetFieldNode.java index 48f85aeeba08..d40945821961 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/AbstractGetFieldNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/AbstractGetFieldNode.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.espresso.nodes.helper; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.CachedContext; import com.oracle.truffle.api.dsl.Specialization; @@ -541,7 +543,13 @@ abstract class ObjectGetFieldNode extends AbstractGetFieldNode { @Override public int getField(VirtualFrame frame, long[] primitives, Object[] refs, BytecodeNode root, StaticObject receiver, int at, int statementIndex) { root.notifyFieldAccess(frame, statementIndex, field, receiver); - BytecodeNode.putObject(refs, at, executeGetField(receiver)); + StaticObject result = executeGetField(receiver); + Assumption noForeignObjects = root.getNoForeignObjects(); + if (noForeignObjects.isValid() && result.isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + BytecodeNode.putObject(refs, at, result); return slotCount; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java index 655614dec120..056caf931073 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java @@ -50,8 +50,6 @@ public final WrapperNode createWrapper(ProbeNode probeNode) { return new BaseQuickNodeWrapper(this, probeNode); } - public abstract boolean producedForeignObject(Object[] refs); - public boolean removedByRedefintion() { return false; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/CheckCastNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/CheckCastNode.java index 231a40a9f8a9..cbc8769eef44 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/CheckCastNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/CheckCastNode.java @@ -43,11 +43,6 @@ public CheckCastNode(Klass typeToCheck, int top, int callerBCI) { this.typeCheckNode = TypeCheckNodeGen.create(typeToCheck.getContext()); } - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } - @Override public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { BytecodeNode root = getBytecodeNode(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/InstanceOfNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/InstanceOfNode.java index b468ee956a39..ad7bdbeda9ef 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/InstanceOfNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/InstanceOfNode.java @@ -49,9 +49,4 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { BytecodeNode.putKind(primitives, refs, top - 1, result, JavaKind.Boolean); return 0; // stack effect -> pop receiver, push boolean } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ArrayLengthNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ArrayLengthNode.java index acd3bb394060..29e342e55d27 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ArrayLengthNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ArrayLengthNode.java @@ -80,9 +80,4 @@ int doForeign(StaticObject array, int doEspresso(StaticObject array) { return InterpreterToVM.arrayLength(array); } - - @Override - public final boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayLoadNode.java index 329caf00957e..92e6163d7161 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayLoadNode.java @@ -89,9 +89,4 @@ byte doForeign(StaticObject array, int index, byte doEspresso(StaticObject array, int index) { return getBytecodeNode().getInterpreterToVM().getArrayByte(index, array); } - - @Override - public final boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayStoreNode.java index d710e2ada889..b7397bdcc604 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ByteArrayStoreNode.java @@ -73,9 +73,4 @@ void doForeign(StaticObject array, int index, byte value, void doEspresso(StaticObject array, int index, byte value) { getBytecodeNode().getInterpreterToVM().setArrayByte(value, index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayLoadNode.java index f54f070a237c..567ba84a0558 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayLoadNode.java @@ -78,9 +78,4 @@ char doForeign(StaticObject array, int index, char doEspresso(StaticObject array, int index) { return getBytecodeNode().getInterpreterToVM().getArrayChar(index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayStoreNode.java index 68ca76eda301..331f2753ab3d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/CharArrayStoreNode.java @@ -67,9 +67,4 @@ void doForeign(StaticObject array, int index, char value, void doEspresso(StaticObject array, int index, char value) { getBytecodeNode().getInterpreterToVM().setArrayChar(value, index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayLoadNode.java index aac75b10bb29..b114d4d385a6 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayLoadNode.java @@ -78,9 +78,4 @@ public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { double doEspresso(StaticObject array, int index) { return getBytecodeNode().getInterpreterToVM().getArrayDouble(index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayStoreNode.java index f7939c3cc33b..fe74f37ee17e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/DoubleArrayStoreNode.java @@ -67,9 +67,4 @@ void doForeign(StaticObject array, int index, double value, void doEspresso(StaticObject array, int index, double value) { getBytecodeNode().getInterpreterToVM().setArrayDouble(value, index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayLoadNode.java index 6ca894eb4d03..4734f1931e14 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayLoadNode.java @@ -78,9 +78,4 @@ float doForeign(StaticObject array, int index, float doEspresso(StaticObject array, int index) { return getBytecodeNode().getInterpreterToVM().getArrayFloat(index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayStoreNode.java index ed7537de3cd9..731fa05b98d3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/FloatArrayStoreNode.java @@ -67,9 +67,4 @@ void doForeign(StaticObject array, int index, float value, void doEspresso(StaticObject array, int index, float value) { getBytecodeNode().getInterpreterToVM().setArrayFloat(value, index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayLoadNode.java index a6b58503aa04..0b662873c3ce 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayLoadNode.java @@ -78,9 +78,4 @@ int doForeign(StaticObject array, int index, int doEspresso(StaticObject array, int index) { return getBytecodeNode().getInterpreterToVM().getArrayInt(index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayStoreNode.java index 4d5c92446042..584bad56b0ac 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/IntArrayStoreNode.java @@ -67,9 +67,4 @@ void doForeign(StaticObject array, int index, int value, void doEspresso(StaticObject array, int index, int value) { getBytecodeNode().getInterpreterToVM().setArrayInt(value, index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayLoadNode.java index 25228264bbc3..41232510ca1c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayLoadNode.java @@ -78,9 +78,4 @@ long doForeign(StaticObject array, int index, long doEspresso(StaticObject array, int index) { return getBytecodeNode().getInterpreterToVM().getArrayLong(index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayStoreNode.java index 63a69a8e60ff..e58f949c32a6 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/LongArrayStoreNode.java @@ -67,9 +67,4 @@ void doForeign(StaticObject array, int index, long value, void doEspresso(StaticObject array, int index, long value) { getBytecodeNode().getInterpreterToVM().setArrayLong(value, index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedGetFieldNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedGetFieldNode.java index 8892b3d377c9..b459bf4cb5de 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedGetFieldNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedGetFieldNode.java @@ -30,7 +30,6 @@ import com.oracle.truffle.espresso.runtime.StaticObject; public final class QuickenedGetFieldNode extends QuickNode { - private final Field field; private final int statementIndex; @Child AbstractGetFieldNode getFieldNode; @@ -39,7 +38,6 @@ public QuickenedGetFieldNode(int top, int callerBCI, int statementIndex, Field f super(top, callerBCI); assert !field.isStatic(); this.getFieldNode = AbstractGetFieldNode.create(field); - this.field = field; this.statementIndex = statementIndex; } @@ -49,9 +47,4 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { StaticObject receiver = nullCheck(BytecodeNode.popObject(refs, top - 1)); return getFieldNode.getField(frame, primitives, refs, root, receiver, top - 1, statementIndex) - 1; // -receiver } - - @Override - public boolean producedForeignObject(Object[] refs) { - return field.getKind().isObject() && BytecodeNode.peekObject(refs, top - 1).isForeignObject(); - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedPutFieldNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedPutFieldNode.java index 667cb029a325..a8f75fdae1c4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedPutFieldNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/QuickenedPutFieldNode.java @@ -50,9 +50,4 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { setFieldNode.setField(frame, primitives, refs, root, receiver, top, statementIndex); return -slotCount - 1; // -receiver } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java index 69b9bbc66619..49b2f8a88fd3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java @@ -23,6 +23,8 @@ package com.oracle.truffle.espresso.nodes.quick.interop; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.CachedContext; import com.oracle.truffle.api.dsl.Specialization; @@ -52,7 +54,13 @@ protected ReferenceArrayLoadNode(int top, int callerBCI) { public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { StaticObject array = nullCheck(BytecodeNode.popObject(refs, top - 2)); int index = BytecodeNode.popInt(primitives, top - 1); - BytecodeNode.putObject(refs, top - 2, executeLoad(array, index)); + StaticObject result = executeLoad(array, index); + Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); + if (noForeignObjects.isValid() && result.isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + BytecodeNode.putObject(refs, top - 2, result); return Bytecodes.stackEffectOf(Bytecodes.AALOAD); } @@ -80,9 +88,4 @@ StaticObject doForeign(StaticObject array, int index, StaticObject doEspresso(StaticObject array, int index) { return getBytecodeNode().getInterpreterToVM().getArrayObject(index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return BytecodeNode.peekObject(refs, top - 2).isForeignObject(); - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayStoreNode.java index e5dda3947194..f672523289c2 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayStoreNode.java @@ -68,9 +68,4 @@ void doForeign(StaticObject array, int index, StaticObject value, void doEspresso(StaticObject array, int index, StaticObject value) { getBytecodeNode().getInterpreterToVM().setArrayObject(value, index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayLoadNode.java index 800b54c79116..fbf6f55be6d0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayLoadNode.java @@ -78,9 +78,4 @@ short doForeign(StaticObject array, int index, short doEspresso(StaticObject array, int index) { return getBytecodeNode().getInterpreterToVM().getArrayShort(index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayStoreNode.java index b11ae66d5a02..748dfb9b95d6 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ShortArrayStoreNode.java @@ -67,9 +67,4 @@ void doForeign(StaticObject array, int index, short value, void doEspresso(StaticObject array, int index, short value) { getBytecodeNode().getInterpreterToVM().setArrayShort(value, index, array); } - - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedGetterNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedGetterNode.java index a2fbff0873e1..4b948e4e72be 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedGetterNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedGetterNode.java @@ -74,11 +74,6 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { return (getResultAt() - top) + getFieldNode.getField(frame, primitives, refs, root, receiver, getResultAt(), statementIndex); } - @Override - public boolean producedForeignObject(Object[] refs) { - return field.getKind().isObject() && BytecodeNode.peekObject(refs, getResultAt()).isForeignObject(); - } - private int getResultAt() { return inlinedMethod.isStatic() ? top : (top - 1); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedSetterNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedSetterNode.java index b872eadf6530..c54d9f97defc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedSetterNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InlinedSetterNode.java @@ -80,11 +80,6 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { return -slotCount + stackEffect; } - @Override - public boolean producedForeignObject(Object[] refs) { - return false; - } - private static Field getInlinedField(Method inlinedMethod) { BytecodeStream code = new BytecodeStream(inlinedMethod.getOriginalCode()); if (inlinedMethod.isStatic()) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeDynamicCallSiteNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeDynamicCallSiteNode.java index 909e3cb64d0d..e73f784534bb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeDynamicCallSiteNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeDynamicCallSiteNode.java @@ -22,12 +22,14 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Type; +import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.meta.Meta; @@ -43,6 +45,7 @@ public final class InvokeDynamicCallSiteNode extends QuickNode { private final JavaKind returnKind; @Child private DirectCallNode callNode; final int resultAt; + final boolean returnsPrimitiveType; @CompilerDirectives.CompilationFinal(dimensions = 1) private Symbol[] parsedSignature; @@ -56,6 +59,7 @@ public InvokeDynamicCallSiteNode(StaticObject memberName, StaticObject appendix, this.hasAppendix = !StaticObject.isNull(appendix); this.callNode = DirectCallNode.create(target.getCallTarget()); this.resultAt = top - Signatures.slotsForParameters(parsedSignature); // no receiver + this.returnsPrimitiveType = Types.isPrimitive(returnType); } @Override @@ -66,6 +70,13 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { args[args.length - 1] = appendix; } Object result = callNode.call(args); + if (!returnsPrimitiveType) { + Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); + if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + } return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), unbasic(result, returnType), returnKind); } @@ -73,11 +84,6 @@ private int getResultAt() { return resultAt; } - @Override - public boolean producedForeignObject(Object[] refs) { - return returnKind.isObject() && BytecodeNode.peekObject(refs, getResultAt()).isForeignObject(); - } - // Transforms ints to sub-words public static Object unbasic(Object arg, Symbol t) { if (t == Type._boolean) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeHandleNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeHandleNode.java index 88ccb84df65e..12c435c315c3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeHandleNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeHandleNode.java @@ -22,17 +22,21 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.espresso.descriptors.Signatures; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Type; +import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.meta.JavaKind; import com.oracle.truffle.espresso.nodes.BytecodeNode; import com.oracle.truffle.espresso.nodes.methodhandle.MethodHandleIntrinsicNode; import com.oracle.truffle.espresso.nodes.quick.QuickNode; +import com.oracle.truffle.espresso.runtime.StaticObject; public final class InvokeHandleNode extends QuickNode { @@ -47,6 +51,7 @@ public final class InvokeHandleNode extends QuickNode { private final int argCount; private final int parameterCount; private final JavaKind rKind; + private final boolean returnsPrimitiveType; public InvokeHandleNode(Method method, Klass accessingKlass, int top, int curBCI) { super(top, curBCI); @@ -58,6 +63,7 @@ public InvokeHandleNode(Method method, Klass accessingKlass, int top, int curBCI this.parameterCount = method.getParameterCount(); this.rKind = method.getReturnKind(); this.resultAt = top - Signatures.slotsForParameters(method.getParsedSignature()) - (hasReceiver ? 1 : 0); // -receiver + this.returnsPrimitiveType = Types.isPrimitive(Signatures.returnType(method.getParsedSignature())); } @Override @@ -68,14 +74,16 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { } BytecodeNode.popBasicArgumentsWithArray(primitives, refs, top, parsedSignature, args, parameterCount, hasReceiver ? 1 : 0); Object result = intrinsic.processReturnValue(intrinsic.call(args), rKind); + if (!returnsPrimitiveType) { + Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); + if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + } return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, method.getReturnKind()); } - @Override - public boolean producedForeignObject(Object[] refs) { - return method.getReturnKind().isObject() && BytecodeNode.peekObject(refs, getResultAt()).isForeignObject(); - } - private int getResultAt() { return resultAt; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java index 8e8fcb888513..9ef84af1056b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; @@ -29,6 +30,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; +import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.impl.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; @@ -44,6 +46,7 @@ public abstract class InvokeInterfaceNode extends QuickNode { final Method resolutionSeed; final Klass declaringKlass; final int resultAt; + final boolean returnsPrimitiveType; static final int INLINE_CACHE_SIZE_LIMIT = 5; @@ -77,6 +80,7 @@ Object callVirtualIndirect(StaticObject receiver, Object[] arguments, this.resolutionSeed = resolutionSeed; this.declaringKlass = resolutionSeed.getDeclaringKlass(); this.resultAt = top - Signatures.slotsForParameters(resolutionSeed.getParsedSignature()) - 1; // -receiver + this.returnsPrimitiveType = Types.isPrimitive(Signatures.returnType(resolutionSeed.getParsedSignature())); } protected static MethodVersion methodLookup(StaticObject receiver, Method resolutionSeed, Klass declaringKlass) { @@ -107,14 +111,16 @@ public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { final Object[] args = BytecodeNode.popArguments(primitives, refs, top, true, resolutionSeed.getParsedSignature()); final StaticObject receiver = nullCheck((StaticObject) args[0]); Object result = executeInterface(receiver, args); + if (!returnsPrimitiveType) { + Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); + if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + } return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, Signatures.returnKind(resolutionSeed.getParsedSignature())); } - @Override - public boolean producedForeignObject(Object[] refs) { - return resolutionSeed.getReturnKind().isObject() && BytecodeNode.peekObject(refs, getResultAt()).isForeignObject(); - } - private int getResultAt() { return resultAt; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java index 31ca7a2710d7..da6888bf97f5 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java @@ -22,11 +22,13 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; +import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.impl.ClassRedefinition; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; @@ -35,16 +37,19 @@ import com.oracle.truffle.espresso.runtime.StaticObject; public final class InvokeSpecialNode extends QuickNode { + @CompilationFinal protected MethodVersion method; @Child private DirectCallNode directCallNode; final int resultAt; + final boolean returnsPrimitiveType; public InvokeSpecialNode(Method method, int top, int callerBCI) { super(top, callerBCI); this.method = method.getMethodVersion(); this.directCallNode = DirectCallNode.create(method.getCallTarget()); this.resultAt = top - Signatures.slotsForParameters(method.getParsedSignature()) - 1; // -receiver + this.returnsPrimitiveType = Types.isPrimitive(Signatures.returnType(method.getParsedSignature())); } @Override @@ -69,14 +74,16 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { } // TODO(peterssen): IsNull Node? Object result = directCallNode.call(args); + if (!returnsPrimitiveType) { + Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); + if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + } return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, method.getMethod().getReturnKind()); } - @Override - public boolean producedForeignObject(Object[] refs) { - return method.getMethod().getReturnKind().isObject() && BytecodeNode.peekObject(refs, getResultAt()).isForeignObject(); - } - private int getResultAt() { return resultAt; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java index c607c3551e60..237a22321f28 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java @@ -22,18 +22,21 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; import com.oracle.truffle.espresso.descriptors.Symbol.Name; +import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.impl.ClassRedefinition; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.nodes.BytecodeNode; import com.oracle.truffle.espresso.nodes.EspressoRootNode; import com.oracle.truffle.espresso.nodes.quick.QuickNode; +import com.oracle.truffle.espresso.runtime.StaticObject; import com.oracle.truffle.espresso.vm.VM; public final class InvokeStaticNode extends QuickNode { @@ -44,6 +47,7 @@ public final class InvokeStaticNode extends QuickNode { @Child private DirectCallNode directCallNode; final int resultAt; + final boolean returnsPrimitiveType; public InvokeStaticNode(Method method, int top, int curBCI) { super(top, curBCI); @@ -53,6 +57,7 @@ public InvokeStaticNode(Method method, int top, int curBCI) { Name.doPrivileged.equals(method.getName()); this.resultAt = top - Signatures.slotsForParameters(method.getParsedSignature()); // no // receiver + this.returnsPrimitiveType = Types.isPrimitive(Signatures.returnType(method.getParsedSignature())); } @Override @@ -88,12 +93,16 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { Object[] args = BytecodeNode.popArguments(primitives, refs, top, false, method.getMethod().getParsedSignature()); Object result = directCallNode.call(args); - return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, method.getMethod().getReturnKind()); - } - @Override - public boolean producedForeignObject(Object[] refs) { - return method.getMethod().getReturnKind().isObject() && BytecodeNode.peekObject(refs, getResultAt()).isForeignObject(); + if (!returnsPrimitiveType) { + Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); + if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + } + + return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, method.getMethod().getReturnKind()); } private int getResultAt() { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java index e590a22782ce..569b60385296 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; @@ -29,6 +30,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.espresso.descriptors.Signatures; +import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.impl.ClassRedefinition; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; @@ -42,6 +44,7 @@ public abstract class InvokeVirtualNode extends QuickNode { final Method resolutionSeed; final int resultAt; + final boolean returnsPrimitiveType; static final int INLINE_CACHE_SIZE_LIMIT = 5; @@ -80,6 +83,7 @@ Object callVirtualIndirect(StaticObject receiver, Object[] arguments, assert !resolutionSeed.isStatic(); this.resolutionSeed = resolutionSeed; this.resultAt = top - Signatures.slotsForParameters(resolutionSeed.getParsedSignature()) - 1; // -receiver; + this.returnsPrimitiveType = Types.isPrimitive(Signatures.returnType(resolutionSeed.getParsedSignature())); } MethodVersion methodLookup(StaticObject receiver) { @@ -108,6 +112,15 @@ public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { Object[] args = BytecodeNode.popArguments(primitives, refs, top, true, resolutionSeed.getParsedSignature()); StaticObject receiver = nullCheck((StaticObject) args[0]); Object result = executeVirtual(receiver, args); + + if (!returnsPrimitiveType) { + Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); + if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + } + return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, resolutionSeed.getReturnKind()); } @@ -116,11 +129,6 @@ public boolean removedByRedefintion() { return resolutionSeed.isRemovedByRedefition(); } - @Override - public final boolean producedForeignObject(Object[] refs) { - return resolutionSeed.getReturnKind().isObject() && BytecodeNode.peekObject(refs, getResultAt()).isForeignObject(); - } - private int getResultAt() { return resultAt; } From a0a7e9ecb6a08bca9552a32b7fbaa98b02a0c221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Wed, 12 May 2021 00:02:33 +0200 Subject: [PATCH 251/290] Use unsigned comparison for bounds check. --- .../nodes/helper/EspressoReferenceArrayStoreNode.java | 2 +- .../com/oracle/truffle/espresso/runtime/StaticObject.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java index 946525035ba4..faa33594944c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/EspressoReferenceArrayStoreNode.java @@ -43,7 +43,7 @@ public EspressoReferenceArrayStoreNode(EspressoContext context) { public void arrayStore(StaticObject value, int index, StaticObject array) { assert !array.isForeignObject(); assert array.isArray(); - if (index >= 0 && index < array.length()) { + if (Integer.compareUnsigned(index, array.length()) < 0) { if (StaticObject.isNull(value) || typeCheck.executeTypeCheck(((ArrayKlass) array.getKlass()).getComponentType(), value.getKlass())) { array.putObjectUnsafe(value, index); } else { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java index 72e26bf56c9d..8a5afcb3d918 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/StaticObject.java @@ -492,7 +492,7 @@ public void putObject(StaticObject value, int index, Meta meta) { public void putObject(StaticObject value, int index, Meta meta, BytecodeNode bytecodeNode) { checkNotForeign(); assert isArray(); - if (index >= 0 && index < length()) { + if (Integer.compareUnsigned(index, length()) < 0) { // TODO(peterssen): Use different profiles for index-out-of-bounds and array-store // exceptions. putObjectUnsafe(arrayStoreExCheck(value, ((ArrayKlass) klass).getComponentType(), meta, bytecodeNode), index); @@ -530,7 +530,7 @@ public void setArrayByte(byte value, int index, Meta meta) { public void setArrayByte(byte value, int index, Meta meta, BytecodeNode bytecodeNode) { checkNotForeign(); assert isArray() && fields instanceof byte[]; - if (index >= 0 && index < length()) { + if (Integer.compareUnsigned(index, length()) < 0) { UNSAFE.putByte(fields, getByteArrayOffset(index), value); } else { if (bytecodeNode != null) { @@ -547,7 +547,7 @@ public byte getArrayByte(int index, Meta meta) { public byte getArrayByte(int index, Meta meta, BytecodeNode bytecodeNode) { checkNotForeign(); assert isArray() && fields instanceof byte[]; - if (index >= 0 && index < length()) { + if (Integer.compareUnsigned(index, length()) < 0) { return UNSAFE.getByte(fields, getByteArrayOffset(index)); } else { if (bytecodeNode != null) { From 08bcc8a2f8096c3864ecc4a025ef1438c1df860b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Wed, 12 May 2021 00:03:08 +0200 Subject: [PATCH 252/290] Hoist locals and operand stack array lengths. --- .../com/oracle/truffle/espresso/nodes/BytecodeNode.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 28dca28ba570..f442e6f86d95 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -669,11 +669,14 @@ Object executeBody(VirtualFrame frame) { } onStart(primitives, refs); + EspressoError.guarantee(primitives.length - 1 < (1 << 17), "hoist"); + EspressoError.guarantee(refs.length - 1 < (1 << 17), "hoist"); + EspressoError.guarantee(primitives.length == refs.length, "hoist"); + loop: while (true) { - int curOpcode; + final int curOpcode = bs.opcode(curBCI); EXECUTED_BYTECODES_COUNT.inc(); try { - curOpcode = bs.currentBC(curBCI); CompilerAsserts.partialEvaluationConstant(top); CompilerAsserts.partialEvaluationConstant(curBCI); CompilerAsserts.partialEvaluationConstant(curOpcode); From dc38af60e14d44b147020e519d1846c4a882debb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Wed, 12 May 2021 00:59:21 +0200 Subject: [PATCH 253/290] Fix BCI increment for bytecodes with the WIDE modifier. --- .../src/com/oracle/truffle/espresso/nodes/BytecodeNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index f442e6f86d95..61f85845b7c0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -1229,7 +1229,7 @@ Object executeBody(VirtualFrame frame) { int targetBCI = bs.nextBCI(curBCI); edgeLocalAnalysis(primitives, refs, curBCI, targetBCI); top += Bytecodes.stackEffectOf(wideOpcode); - curBCI += targetBCI; + curBCI = targetBCI; continue loop; } From 3a13e735c3edf5d6a599ec2a22aa1abbe1134e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Wed, 12 May 2021 02:03:16 +0200 Subject: [PATCH 254/290] Document numeric suffixes in BytecodeStream. --- .../com/oracle/truffle/espresso/bytecode/BytecodeStream.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/bytecode/BytecodeStream.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/bytecode/BytecodeStream.java index 758285a755e7..b27d386034bc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/bytecode/BytecodeStream.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/bytecode/BytecodeStream.java @@ -40,7 +40,9 @@ * prone. For example, it handles the {@link Bytecodes#WIDE} instruction and wide variants of * instructions internally. * - * Some operations have suffix wi + * Some accessors have a suffix indicating the type of bytecode it handles, these do NOT + * handle the {@link Bytecodes#WIDE} modifier. Methods without the numeric suffix will handle the + * {@link Bytecodes#WIDE} modifier internally, but may be slower. */ public final class BytecodeStream { From 5af8da988b73c94deb29a8be634e1983fc01e314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Wed, 12 May 2021 02:04:04 +0200 Subject: [PATCH 255/290] Simplify the invalidation of noForeignObjects assumption. --- .../truffle/espresso/nodes/BytecodeNode.java | 34 +++++++------------ .../nodes/helper/AbstractGetFieldNode.java | 8 +---- .../quick/interop/ReferenceArrayLoadNode.java | 8 +---- .../invoke/InvokeDynamicCallSiteNode.java | 7 +--- .../nodes/quick/invoke/InvokeHandleNode.java | 8 +---- .../quick/invoke/InvokeInterfaceNode.java | 16 +++------ .../nodes/quick/invoke/InvokeSpecialNode.java | 7 +--- .../nodes/quick/invoke/InvokeStaticNode.java | 9 +---- .../nodes/quick/invoke/InvokeVirtualNode.java | 16 +++------ 9 files changed, 29 insertions(+), 84 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 61f85845b7c0..47e54f5003ed 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -396,10 +396,6 @@ public final class BytecodeNode extends EspressoMethodNode { @Child private volatile InstrumentationSupport instrumentation; - public Assumption getNoForeignObjects() { - return noForeignObjects; - } - private final Assumption noForeignObjects; // Cheap profile for implicit exceptions e.g. null checks, division by 0, index out of bounds. @@ -455,10 +451,7 @@ private void initArguments(Object[] arguments, long[] primitives, Object[] refs) assert StaticObject.notNull((StaticObject) arguments[0]) : "null receiver in init arguments !"; StaticObject receiver = (StaticObject) arguments[0]; setLocalObject(refs, curSlot, receiver); - if (noForeignObjects.isValid() && receiver.isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + checkNoForeignObjectAssumption(receiver); curSlot += JavaKind.Object.getSlotCount(); } @@ -485,16 +478,21 @@ private void initArguments(Object[] arguments, long[] primitives, Object[] refs) // @formatter:on } else { // Reference type. - setLocalObject(refs, curSlot, (StaticObject) arguments[i + receiverSlot]); - if (noForeignObjects.isValid() && ((StaticObject) arguments[i + receiverSlot]).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + StaticObject argument = (StaticObject) arguments[i + receiverSlot]; + setLocalObject(refs, curSlot, argument); + checkNoForeignObjectAssumption(argument); } ++curSlot; } } + public void checkNoForeignObjectAssumption(StaticObject object) { + if (noForeignObjects.isValid() && object.isForeignObject()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + noForeignObjects.invalidate(); + } + } + private void setBCI(VirtualFrame frame, int bci) { frame.setInt(bciSlot, bci); } @@ -791,10 +789,7 @@ Object executeBody(VirtualFrame frame) { case SALOAD: arrayLoad(frame, primitives, refs, top, curBCI, curOpcode); break; case AALOAD: arrayLoad(frame, primitives, refs, top, curBCI, AALOAD); - if (noForeignObjects.isValid() && peekObject(refs, top - 2).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + checkNoForeignObjectAssumption(peekObject(refs, top - 2)); break; case ISTORE: @@ -2503,10 +2498,7 @@ private int getField(VirtualFrame frame, long[] primitives, Object[] refs, int t case Object : { StaticObject value = InterpreterToVM.getFieldObject(receiver, field); putObject(refs, resultAt, value); - if (noForeignObjects.isValid() && value.isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + checkNoForeignObjectAssumption(value); break; } default : diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/AbstractGetFieldNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/AbstractGetFieldNode.java index d40945821961..f0d9bd812dcc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/AbstractGetFieldNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/helper/AbstractGetFieldNode.java @@ -22,8 +22,6 @@ */ package com.oracle.truffle.espresso.nodes.helper; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.CachedContext; import com.oracle.truffle.api.dsl.Specialization; @@ -544,11 +542,7 @@ abstract class ObjectGetFieldNode extends AbstractGetFieldNode { public int getField(VirtualFrame frame, long[] primitives, Object[] refs, BytecodeNode root, StaticObject receiver, int at, int statementIndex) { root.notifyFieldAccess(frame, statementIndex, field, receiver); StaticObject result = executeGetField(receiver); - Assumption noForeignObjects = root.getNoForeignObjects(); - if (noForeignObjects.isValid() && result.isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + root.checkNoForeignObjectAssumption(result); BytecodeNode.putObject(refs, at, result); return slotCount; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java index 49b2f8a88fd3..66489dede0fc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/interop/ReferenceArrayLoadNode.java @@ -23,8 +23,6 @@ package com.oracle.truffle.espresso.nodes.quick.interop; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.CachedContext; import com.oracle.truffle.api.dsl.Specialization; @@ -55,11 +53,7 @@ public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { StaticObject array = nullCheck(BytecodeNode.popObject(refs, top - 2)); int index = BytecodeNode.popInt(primitives, top - 1); StaticObject result = executeLoad(array, index); - Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); - if (noForeignObjects.isValid() && result.isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + getBytecodeNode().checkNoForeignObjectAssumption(result); BytecodeNode.putObject(refs, top - 2, result); return Bytecodes.stackEffectOf(Bytecodes.AALOAD); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeDynamicCallSiteNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeDynamicCallSiteNode.java index e73f784534bb..2f328e4a7ae3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeDynamicCallSiteNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeDynamicCallSiteNode.java @@ -22,7 +22,6 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; -import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; @@ -71,11 +70,7 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { } Object result = callNode.call(args); if (!returnsPrimitiveType) { - Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); - if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + getBytecodeNode().checkNoForeignObjectAssumption((StaticObject) result); } return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), unbasic(result, returnType), returnKind); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeHandleNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeHandleNode.java index 12c435c315c3..3762a3e06100 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeHandleNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeHandleNode.java @@ -22,8 +22,6 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.espresso.descriptors.Signatures; @@ -75,11 +73,7 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { BytecodeNode.popBasicArgumentsWithArray(primitives, refs, top, parsedSignature, args, parameterCount, hasReceiver ? 1 : 0); Object result = intrinsic.processReturnValue(intrinsic.call(args), rKind); if (!returnsPrimitiveType) { - Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); - if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + getBytecodeNode().checkNoForeignObjectAssumption((StaticObject) result); } return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, method.getReturnKind()); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java index 9ef84af1056b..a37db031b8f1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeInterfaceNode.java @@ -22,7 +22,6 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; -import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; @@ -103,20 +102,15 @@ protected static MethodVersion methodLookup(StaticObject receiver, Method resolu @Override public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - // Method signature does not change across methods. - // Can safely use the constant signature from `resolutionSeed` instead of the non-constant - // signature from the lookup. - // TODO(peterssen): Maybe refrain from exposing the whole root node?. - // TODO(peterssen): IsNull Node?. + /** + * Method signature does not change across methods. Can safely use the constant signature + * from `resolutionSeed` instead of the non-constant signature from the lookup. + */ final Object[] args = BytecodeNode.popArguments(primitives, refs, top, true, resolutionSeed.getParsedSignature()); final StaticObject receiver = nullCheck((StaticObject) args[0]); Object result = executeInterface(receiver, args); if (!returnsPrimitiveType) { - Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); - if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + getBytecodeNode().checkNoForeignObjectAssumption((StaticObject) result); } return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, Signatures.returnKind(resolutionSeed.getParsedSignature())); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java index da6888bf97f5..e95967af0188 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeSpecialNode.java @@ -22,7 +22,6 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; -import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.VirtualFrame; @@ -75,11 +74,7 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { // TODO(peterssen): IsNull Node? Object result = directCallNode.call(args); if (!returnsPrimitiveType) { - Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); - if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + getBytecodeNode().checkNoForeignObjectAssumption((StaticObject) result); } return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, method.getMethod().getReturnKind()); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java index 237a22321f28..64435ff65d6f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeStaticNode.java @@ -22,7 +22,6 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; -import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.VirtualFrame; @@ -93,15 +92,9 @@ public int execute(VirtualFrame frame, long[] primitives, Object[] refs) { Object[] args = BytecodeNode.popArguments(primitives, refs, top, false, method.getMethod().getParsedSignature()); Object result = directCallNode.call(args); - if (!returnsPrimitiveType) { - Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); - if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + getBytecodeNode().checkNoForeignObjectAssumption((StaticObject) result); } - return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, method.getMethod().getReturnKind()); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java index 569b60385296..dafbe4da8c3a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/InvokeVirtualNode.java @@ -22,7 +22,6 @@ */ package com.oracle.truffle.espresso.nodes.quick.invoke; -import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; @@ -106,21 +105,16 @@ MethodVersion methodLookup(StaticObject receiver) { @Override public final int execute(VirtualFrame frame, long[] primitives, Object[] refs) { - // Method signature does not change across methods. - // Can safely use the constant signature from `resolutionSeed` instead of the non-constant - // signature from the lookup. + /* + * Method signature does not change across methods. Can safely use the constant signature + * from `resolutionSeed` instead of the non-constant signature from the lookup. + */ Object[] args = BytecodeNode.popArguments(primitives, refs, top, true, resolutionSeed.getParsedSignature()); StaticObject receiver = nullCheck((StaticObject) args[0]); Object result = executeVirtual(receiver, args); - if (!returnsPrimitiveType) { - Assumption noForeignObjects = getBytecodeNode().getNoForeignObjects(); - if (noForeignObjects.isValid() && ((StaticObject) result).isForeignObject()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - noForeignObjects.invalidate(); - } + getBytecodeNode().checkNoForeignObjectAssumption((StaticObject) result); } - return (getResultAt() - top) + BytecodeNode.putKind(primitives, refs, getResultAt(), result, resolutionSeed.getReturnKind()); } From 3af32dd65bdeff529fa8f0ce1a0e1b5487fc9f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Thu, 13 May 2021 12:21:00 +0200 Subject: [PATCH 256/290] Save bci before executing trapping bytecodes. --- .../oracle/truffle/espresso/nodes/BytecodeNode.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 47e54f5003ed..02f0bfb44e25 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -686,10 +686,13 @@ Object executeBody(VirtualFrame frame) { CompilerDirectives.ensureVirtualized(refs); if (instrument != null) { - setBCI(frame, curBCI); instrument.notifyStatement(frame, statementIndex, nextStatementIndex); statementIndex = nextStatementIndex; } + if (instrument != null || Bytecodes.canTrap(curOpcode)) { + // curOpcode can be == WIDE, but none of the WIDE-prefixed bytecodes throw exceptions. + setBCI(frame, curBCI); + } // @formatter:off switch (curOpcode) { @@ -1153,7 +1156,6 @@ Object executeBody(VirtualFrame frame) { case INVOKESPECIAL: // fall through case INVOKESTATIC: // fall through case INVOKEINTERFACE: - setBCI(frame, curBCI); top += quickenInvoke(frame, primitives, refs, top, curBCI, curOpcode, statementIndex); break; case NEW : putObject(refs, top, InterpreterToVM.newObject(resolveType(NEW, readCPI(curBCI)), true)); break; @@ -1163,7 +1165,6 @@ Object executeBody(VirtualFrame frame) { case ARRAYLENGTH : arrayLength(frame, primitives, refs, top, curBCI); break; case ATHROW : - setBCI(frame, curBCI); throw getMeta().throwException(nullCheck(popObject(refs, top - 1))); case CHECKCAST : top += quickenCheckCast(frame, primitives, refs, top, curBCI, CHECKCAST); break; @@ -1235,12 +1236,10 @@ Object executeBody(VirtualFrame frame) { throw EspressoError.unimplemented(Bytecodes.nameOf(curOpcode) + " not supported."); case INVOKEDYNAMIC: - setBCI(frame, curBCI); top += quickenInvokeDynamic(frame, primitives, refs, top, curBCI, INVOKEDYNAMIC); break; case QUICK: { - setBCI(frame, curBCI); // Force a volatile read of the opcode. if (bs.currentVolatileBC(curBCI) != QUICK) { // Possible case of read reordering. Retry handling the bytecode to make sure we get a correct CPI. @@ -1273,7 +1272,6 @@ Object executeBody(VirtualFrame frame) { break; } case SLIM_QUICK: - setBCI(frame, curBCI); top += sparseNodes[curBCI].execute(frame, primitives, refs); break; @@ -1283,7 +1281,6 @@ Object executeBody(VirtualFrame frame) { } // @formatter:on } catch (EspressoException | AbstractTruffleException | StackOverflowError | OutOfMemoryError e) { - setBCI(frame, curBCI); if (instrument != null && e instanceof EspressoException) { instrument.notifyExceptionAt(frame, e, statementIndex); } @@ -1378,7 +1375,6 @@ Object executeBody(VirtualFrame frame) { } } catch (EspressoExitException e) { CompilerDirectives.transferToInterpreter(); - setBCI(frame, curBCI); getRoot().abortMonitor(frame); // Tearing down the VM, no need to report loop count. throw e; From dc5f0369e48303ea1e08c6b3ea77dc74132e475d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Thu, 13 May 2021 22:36:22 +0200 Subject: [PATCH 257/290] Simpilfy return bytecodes. --- .../truffle/espresso/nodes/BytecodeNode.java | 83 +++++++++---------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 02f0bfb44e25..ca926465449b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -288,6 +288,7 @@ import com.oracle.truffle.espresso.descriptors.Signatures; import com.oracle.truffle.espresso.descriptors.Symbol; import com.oracle.truffle.espresso.descriptors.Symbol.Type; +import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.impl.ArrayKlass; import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Klass; @@ -690,7 +691,10 @@ Object executeBody(VirtualFrame frame) { statementIndex = nextStatementIndex; } if (instrument != null || Bytecodes.canTrap(curOpcode)) { - // curOpcode can be == WIDE, but none of the WIDE-prefixed bytecodes throw exceptions. + /* + * curOpcode can be == WIDE, but none of the WIDE-prefixed bytecodes throw + * exceptions. + */ setBCI(frame, curBCI); } @@ -1135,12 +1139,22 @@ Object executeBody(VirtualFrame frame) { continue loop; } // @formatter:off - case IRETURN: return notifyReturn(frame, statementIndex, exitMethodAndReturn(popInt(primitives, top - 1)), loopCount[0]); - case LRETURN: return notifyReturn(frame, statementIndex, exitMethodAndReturnObject(popLong(primitives, top - 1)), loopCount[0]); - case FRETURN: return notifyReturn(frame, statementIndex, exitMethodAndReturnObject(popFloat(primitives, top - 1)), loopCount[0]); - case DRETURN: return notifyReturn(frame, statementIndex, exitMethodAndReturnObject(popDouble(primitives, top - 1)), loopCount[0]); - case ARETURN: return notifyReturn(frame, statementIndex, exitMethodAndReturnObject(popObject(refs, top - 1)), loopCount[0]); - case RETURN : return notifyReturn(frame, statementIndex, exitMethodAndReturn(), loopCount[0]); + + case IRETURN: // fall through + case LRETURN: // fall through + case FRETURN: // fall through + case DRETURN: // fall through + case ARETURN: // fall through + case RETURN : { + if (loopCount[0] > 0) { + LoopNode.reportLoopCount(this, loopCount[0]); + } + Object returnValue = getReturnValueAsObject(primitives, refs, top); + if (instrument != null) { + instrument.notifyReturn(frame, statementIndex, returnValue); + } + return returnValue; + } // TODO(peterssen): Order shuffled. case GETSTATIC : // fall through @@ -1391,6 +1405,26 @@ Object executeBody(VirtualFrame frame) { } } + private Object getReturnValueAsObject(long[] primitives, Object[] refs, int top) { + Symbol returnType = Signatures.returnType(getMethod().getParsedSignature()); + // @formatter:off + switch (returnType.byteAt(0)) { + case 'Z' : return stackIntToBoolean(popInt(primitives, top - 1)); + case 'B' : return (byte) popInt(primitives, top - 1); + case 'S' : return (short) popInt(primitives, top - 1); + case 'C' : return (char) popInt(primitives, top - 1); + case 'I' : return popInt(primitives, top - 1); + case 'J' : return popLong(primitives, top - 1); + case 'F' : return popFloat(primitives, top - 1); + case 'D' : return popDouble(primitives, top - 1); + case 'V' : return StaticObject.NULL; // void + default: + assert Types.isReference(returnType); + return popObject(refs, top - 1); + } + // @formatter:on + } + @ExplodeLoop private static void clearOperandStack(long[] primitives, Object[] refs, int top) { for (int slot = top - 1; slot >= 0; --slot) { @@ -1429,16 +1463,6 @@ public int getBci(Frame frame) { } } - private Object notifyReturn(VirtualFrame frame, int statementIndex, Object toReturn, int loopCount) { - if (loopCount > 0) { - LoopNode.reportLoopCount(this, loopCount); - } - if (instrumentation != null) { - instrumentation.notifyReturn(frame, statementIndex, toReturn); - } - return toReturn; - } - @Override public InstrumentableNode materializeInstrumentableNodes(Set> materializedTags) { InstrumentationSupport info = this.instrumentation; @@ -2116,35 +2140,10 @@ private int allocateMultiArray(long[] primitives, Object[] refs, int top, Klass // region Method return - private Object exitMethodAndReturn(int result) { - // @formatter:off - switch (Signatures.returnKind(getMethod().getParsedSignature())) { - case Boolean : - // TODO: Use a node ? - return stackIntToBoolean(result); - case Byte : return (byte) result; - case Short : return (short) result; - case Char : return (char) result; - case Int : return result; - default : - CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere("unexpected kind"); - } - // @formatter:on - } - private boolean stackIntToBoolean(int result) { return getJavaVersion().java9OrLater() ? (result & 1) != 0 : result != 0; } - private static Object exitMethodAndReturnObject(Object result) { - return result; - } - - private static Object exitMethodAndReturn() { - return exitMethodAndReturnObject(StaticObject.NULL); - } - // endregion Method return // region Arithmetic/binary operations From 7c9813d73c8c41e0da79d562a8d1f0bd192ba221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=C2=B2=20Peterssen?= Date: Fri, 14 May 2021 13:29:46 +0200 Subject: [PATCH 258/290] Save the current bci before triggering the debugger hook. --- .../com/oracle/truffle/espresso/nodes/BytecodeNode.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index ca926465449b..7a9e0ba5e68a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -686,10 +686,6 @@ Object executeBody(VirtualFrame frame) { CompilerDirectives.ensureVirtualized(primitives); CompilerDirectives.ensureVirtualized(refs); - if (instrument != null) { - instrument.notifyStatement(frame, statementIndex, nextStatementIndex); - statementIndex = nextStatementIndex; - } if (instrument != null || Bytecodes.canTrap(curOpcode)) { /* * curOpcode can be == WIDE, but none of the WIDE-prefixed bytecodes throw @@ -697,6 +693,10 @@ Object executeBody(VirtualFrame frame) { */ setBCI(frame, curBCI); } + if (instrument != null) { + instrument.notifyStatement(frame, statementIndex, nextStatementIndex); + statementIndex = nextStatementIndex; + } // @formatter:off switch (curOpcode) { From 2592dcb8db6097ab1826b38e74a12b410dc679ba Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Thu, 6 May 2021 14:53:44 +0200 Subject: [PATCH 259/290] Bench suite added automatically by mx --- vm/mx.vm/mx_vm_benchmark.py | 8 -------- vm/mx.vm/suite.py | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/vm/mx.vm/mx_vm_benchmark.py b/vm/mx.vm/mx_vm_benchmark.py index f0f3efb5fc0c..818cd3c9d1db 100644 --- a/vm/mx.vm/mx_vm_benchmark.py +++ b/vm/mx.vm/mx_vm_benchmark.py @@ -390,7 +390,6 @@ class NativeImageHexToInt(object): def __call__(self, *args, **kwargs): return int(args[0], 16) - suiteName = self.bmSuite.name() if self.bmSuite else "" return [ mx_benchmark.StdOutRule( r"The executed image size for benchmark (?P[a-zA-Z0-9_\-]+):(?P[a-zA-Z0-9_\-]+) is (?P[0-9]+) B", @@ -422,7 +421,6 @@ def __call__(self, *args, **kwargs): "metric.object": ("", str) }), mx_benchmark.StdOutRule(r'^\[\S+:[0-9]+\][ ]+\[total\]:[ ]+(?P