From b2a0b94b3603852f43ee1fd2ef1fd45d32613564 Mon Sep 17 00:00:00 2001 From: Mike Duigou Date: Tue, 3 May 2011 16:32:47 -0700 Subject: [PATCH 01/65] 7041612: Rename StandardCharset to StandardCharsets Reviewed-by: alanb, mr, darcy --- jdk/make/java/nio/FILES_java.gmk | 2 +- jdk/src/share/classes/java/nio/charset/Charset.java | 2 +- .../{StandardCharset.java => StandardCharsets.java} | 6 +++--- jdk/src/share/classes/java/nio/file/Path.java | 2 +- jdk/src/share/classes/java/util/zip/ZipCoder.java | 8 ++++---- jdk/src/share/classes/java/util/zip/ZipFile.java | 4 ++-- jdk/src/share/classes/java/util/zip/ZipInputStream.java | 4 ++-- jdk/src/share/classes/java/util/zip/ZipOutputStream.java | 4 ++-- jdk/src/share/classes/sun/awt/FontDescriptor.java | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) rename jdk/src/share/classes/java/nio/charset/{StandardCharset.java => StandardCharsets.java} (96%) diff --git a/jdk/make/java/nio/FILES_java.gmk b/jdk/make/java/nio/FILES_java.gmk index d09a966db38..f50f2fd1efb 100644 --- a/jdk/make/java/nio/FILES_java.gmk +++ b/jdk/make/java/nio/FILES_java.gmk @@ -71,7 +71,7 @@ FILES_src = \ java/nio/charset/CoderMalfunctionError.java \ java/nio/charset/CodingErrorAction.java \ java/nio/charset/MalformedInputException.java \ - java/nio/charset/StandardCharset.java \ + java/nio/charset/StandardCharsets.java \ java/nio/charset/UnmappableCharacterException.java \ \ java/nio/charset/spi/CharsetProvider.java \ diff --git a/jdk/src/share/classes/java/nio/charset/Charset.java b/jdk/src/share/classes/java/nio/charset/Charset.java index 51dbe1504dc..1a30ba96103 100644 --- a/jdk/src/share/classes/java/nio/charset/Charset.java +++ b/jdk/src/share/classes/java/nio/charset/Charset.java @@ -215,7 +215,7 @@ import sun.security.action.GetPropertyAction; * determined during virtual-machine startup and typically depends upon the * locale and charset being used by the underlying operating system.

* - *

The {@link StandardCharset} class defines constants for each of the + *

The {@link StandardCharsets} class defines constants for each of the * standard charsets. * *

Terminology

diff --git a/jdk/src/share/classes/java/nio/charset/StandardCharset.java b/jdk/src/share/classes/java/nio/charset/StandardCharsets.java similarity index 96% rename from jdk/src/share/classes/java/nio/charset/StandardCharset.java rename to jdk/src/share/classes/java/nio/charset/StandardCharsets.java index edec058bc45..b2572c361cf 100644 --- a/jdk/src/share/classes/java/nio/charset/StandardCharset.java +++ b/jdk/src/share/classes/java/nio/charset/StandardCharsets.java @@ -32,10 +32,10 @@ package java.nio.charset; * @see Standard Charsets * @since 1.7 */ -public final class StandardCharset { +public final class StandardCharsets { - private StandardCharset() { - throw new AssertionError("No java.nio.charset.StandardCharset instances for you!"); + private StandardCharsets() { + throw new AssertionError("No java.nio.charset.StandardCharsets instances for you!"); } /** * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the diff --git a/jdk/src/share/classes/java/nio/file/Path.java b/jdk/src/share/classes/java/nio/file/Path.java index 2d2e977d49d..69e5086a1e1 100644 --- a/jdk/src/share/classes/java/nio/file/Path.java +++ b/jdk/src/share/classes/java/nio/file/Path.java @@ -72,7 +72,7 @@ import java.util.Iterator; * directory and is UTF-8 encoded. *
  *     Path path = FileSystems.getDefault().getPath("logs", "access.log");
- *     BufferReader reader = Files.newBufferedReader(path, StandardCharset.UTF_8);
+ *     BufferReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
  * 
* *

Interoperability

diff --git a/jdk/src/share/classes/java/util/zip/ZipCoder.java b/jdk/src/share/classes/java/util/zip/ZipCoder.java index 62844916f3d..12404cb38ac 100644 --- a/jdk/src/share/classes/java/util/zip/ZipCoder.java +++ b/jdk/src/share/classes/java/util/zip/ZipCoder.java @@ -28,7 +28,7 @@ package java.util.zip; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; -import java.nio.charset.StandardCharset; +import java.nio.charset.StandardCharsets; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; @@ -107,7 +107,7 @@ final class ZipCoder { if (isUTF8) return getBytes(s); if (utf8 == null) - utf8 = new ZipCoder(StandardCharset.UTF_8); + utf8 = new ZipCoder(StandardCharsets.UTF_8); return utf8.getBytes(s); } @@ -116,7 +116,7 @@ final class ZipCoder { if (isUTF8) return toString(ba, len); if (utf8 == null) - utf8 = new ZipCoder(StandardCharset.UTF_8); + utf8 = new ZipCoder(StandardCharsets.UTF_8); return utf8.toString(ba, len); } @@ -132,7 +132,7 @@ final class ZipCoder { private ZipCoder(Charset cs) { this.cs = cs; - this.isUTF8 = cs.name().equals(StandardCharset.UTF_8.name()); + this.isUTF8 = cs.name().equals(StandardCharsets.UTF_8.name()); } static ZipCoder get(Charset charset) { diff --git a/jdk/src/share/classes/java/util/zip/ZipFile.java b/jdk/src/share/classes/java/util/zip/ZipFile.java index d1861ad732c..9693809331a 100644 --- a/jdk/src/share/classes/java/util/zip/ZipFile.java +++ b/jdk/src/share/classes/java/util/zip/ZipFile.java @@ -31,7 +31,7 @@ import java.io.IOException; import java.io.EOFException; import java.io.File; import java.nio.charset.Charset; -import java.nio.charset.StandardCharset; +import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.Deque; import java.util.Enumeration; @@ -141,7 +141,7 @@ class ZipFile implements ZipConstants, Closeable { * @since 1.3 */ public ZipFile(File file, int mode) throws IOException { - this(file, mode, StandardCharset.UTF_8); + this(file, mode, StandardCharsets.UTF_8); } /** diff --git a/jdk/src/share/classes/java/util/zip/ZipInputStream.java b/jdk/src/share/classes/java/util/zip/ZipInputStream.java index ebfcce146cc..a60adb8040e 100644 --- a/jdk/src/share/classes/java/util/zip/ZipInputStream.java +++ b/jdk/src/share/classes/java/util/zip/ZipInputStream.java @@ -30,7 +30,7 @@ import java.io.IOException; import java.io.EOFException; import java.io.PushbackInputStream; import java.nio.charset.Charset; -import java.nio.charset.StandardCharset; +import java.nio.charset.StandardCharsets; import static java.util.zip.ZipConstants64.*; /** @@ -76,7 +76,7 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants { * @param in the actual input stream */ public ZipInputStream(InputStream in) { - this(in, StandardCharset.UTF_8); + this(in, StandardCharsets.UTF_8); } /** diff --git a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java index c0b59488002..91f7f75ece2 100644 --- a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java +++ b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java @@ -28,7 +28,7 @@ package java.util.zip; import java.io.OutputStream; import java.io.IOException; import java.nio.charset.Charset; -import java.nio.charset.StandardCharset; +import java.nio.charset.StandardCharsets; import java.util.Vector; import java.util.HashSet; import static java.util.zip.ZipConstants64.*; @@ -101,7 +101,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { * @param out the actual output stream */ public ZipOutputStream(OutputStream out) { - this(out, StandardCharset.UTF_8); + this(out, StandardCharsets.UTF_8); } /** diff --git a/jdk/src/share/classes/sun/awt/FontDescriptor.java b/jdk/src/share/classes/sun/awt/FontDescriptor.java index e98ec65e029..fc1df5a2a50 100644 --- a/jdk/src/share/classes/sun/awt/FontDescriptor.java +++ b/jdk/src/share/classes/sun/awt/FontDescriptor.java @@ -26,7 +26,7 @@ package sun.awt; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; -import java.nio.charset.StandardCharset; +import java.nio.charset.StandardCharsets; import sun.nio.cs.HistoricallyNamedCharset; public class FontDescriptor implements Cloneable { @@ -105,8 +105,8 @@ public class FontDescriptor implements Cloneable { if (useUnicode && unicodeEncoder == null) { try { this.unicodeEncoder = isLE? - StandardCharset.UTF_16LE.newEncoder(): - StandardCharset.UTF_16BE.newEncoder(); + StandardCharsets.UTF_16LE.newEncoder(): + StandardCharsets.UTF_16BE.newEncoder(); } catch (IllegalArgumentException x) {} } return useUnicode; From 116ccec7a04e6908b226fa90056fa12156534d34 Mon Sep 17 00:00:00 2001 From: Pavel Porvatov Date: Wed, 4 May 2011 10:20:14 +0400 Subject: [PATCH 02/65] 7031551: Generics: JComboBox Reviewed-by: alexp, malenkov --- .../classes/javax/swing/ComboBoxModel.java | 4 +- .../javax/swing/DefaultComboBoxModel.java | 33 ++--- .../share/classes/javax/swing/JComboBox.java | 72 +++++----- .../javax/swing/MutableComboBoxModel.java | 14 +- .../swing/plaf/basic/BasicDirectoryModel.java | 2 +- .../swing/plaf/metal/MetalFileChooserUI.java | 4 +- .../swing/JComboBox/7031551/bug7031551.java | 123 ++++++++++++++++++ 7 files changed, 185 insertions(+), 67 deletions(-) create mode 100644 jdk/test/javax/swing/JComboBox/7031551/bug7031551.java diff --git a/jdk/src/share/classes/javax/swing/ComboBoxModel.java b/jdk/src/share/classes/javax/swing/ComboBoxModel.java index 78ac525ec7d..4b657daf881 100644 --- a/jdk/src/share/classes/javax/swing/ComboBoxModel.java +++ b/jdk/src/share/classes/javax/swing/ComboBoxModel.java @@ -33,9 +33,11 @@ package javax.swing; * ListModel. This disjoint behavior allows for the temporary * storage and retrieval of a selected item in the model. * + * @param the type of the elements of this model + * * @author Arnaud Weber */ -public interface ComboBoxModel extends ListModel { +public interface ComboBoxModel extends ListModel { /** * Set the selected item. The implementation of this method should notify diff --git a/jdk/src/share/classes/javax/swing/DefaultComboBoxModel.java b/jdk/src/share/classes/javax/swing/DefaultComboBoxModel.java index f7cc50b1a70..f0266fc7351 100644 --- a/jdk/src/share/classes/javax/swing/DefaultComboBoxModel.java +++ b/jdk/src/share/classes/javax/swing/DefaultComboBoxModel.java @@ -24,39 +24,28 @@ */ package javax.swing; -import java.beans.*; import java.util.*; -import java.awt.*; -import java.awt.event.*; - import java.io.Serializable; -import java.io.ObjectOutputStream; -import java.io.ObjectInputStream; -import java.io.IOException; - -import javax.swing.event.*; -import javax.swing.plaf.*; -import javax.swing.border.*; - -import javax.accessibility.*; /** * The default model for combo boxes. * + * @param the type of the elements of this model + * * @author Arnaud Weber * @author Tom Santos */ -public class DefaultComboBoxModel extends AbstractListModel implements MutableComboBoxModel, Serializable { - Vector objects; +public class DefaultComboBoxModel extends AbstractListModel implements MutableComboBoxModel, Serializable { + Vector objects; Object selectedObject; /** * Constructs an empty DefaultComboBoxModel object. */ public DefaultComboBoxModel() { - objects = new Vector(); + objects = new Vector(); } /** @@ -65,8 +54,8 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo * * @param items an array of Object objects */ - public DefaultComboBoxModel(final Object items[]) { - objects = new Vector(); + public DefaultComboBoxModel(final E items[]) { + objects = new Vector(); objects.ensureCapacity( items.length ); int i,c; @@ -84,7 +73,7 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo * * @param v a Vector object ... */ - public DefaultComboBoxModel(Vector v) { + public DefaultComboBoxModel(Vector v) { objects = v; if ( getSize() > 0 ) { @@ -117,7 +106,7 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo } // implements javax.swing.ListModel - public Object getElementAt(int index) { + public E getElementAt(int index) { if ( index >= 0 && index < objects.size() ) return objects.elementAt(index); else @@ -136,7 +125,7 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo } // implements javax.swing.MutableComboBoxModel - public void addElement(Object anObject) { + public void addElement(E anObject) { objects.addElement(anObject); fireIntervalAdded(this,objects.size()-1, objects.size()-1); if ( objects.size() == 1 && selectedObject == null && anObject != null ) { @@ -145,7 +134,7 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo } // implements javax.swing.MutableComboBoxModel - public void insertElementAt(Object anObject,int index) { + public void insertElementAt(E anObject,int index) { objects.insertElementAt(anObject,index); fireIntervalAdded(this, index, index); } diff --git a/jdk/src/share/classes/javax/swing/JComboBox.java b/jdk/src/share/classes/javax/swing/JComboBox.java index b4a414bbf1a..8ac710910a8 100644 --- a/jdk/src/share/classes/javax/swing/JComboBox.java +++ b/jdk/src/share/classes/javax/swing/JComboBox.java @@ -69,6 +69,8 @@ import javax.accessibility.*; * @see ComboBoxModel * @see DefaultComboBoxModel * + * @param the type of the elements of this combo box + * * @beaninfo * attribute: isContainer false * description: A combination of a text field and a drop-down list. @@ -76,7 +78,7 @@ import javax.accessibility.*; * @author Arnaud Weber * @author Mark Davidson */ -public class JComboBox extends JComponent +public class JComboBox extends JComponent implements ItemSelectable,ListDataListener,ActionListener, Accessible { /** * @see #getUIClassID @@ -91,7 +93,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @see #getModel * @see #setModel */ - protected ComboBoxModel dataModel; + protected ComboBoxModel dataModel; /** * This protected field is implementation specific. Do not access directly * or override. Use the accessor methods instead. @@ -99,7 +101,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @see #getRenderer * @see #setRenderer */ - protected ListCellRenderer renderer; + protected ListCellRenderer renderer; /** * This protected field is implementation specific. Do not access directly * or override. Use the accessor methods instead. @@ -156,7 +158,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { */ protected Object selectedItemReminder = null; - private Object prototypeDisplayValue; + private E prototypeDisplayValue; // Flag to ensure that infinite loops do not occur with ActionEvents. private boolean firingActionEvent = false; @@ -175,7 +177,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * displayed list of items * @see DefaultComboBoxModel */ - public JComboBox(ComboBoxModel aModel) { + public JComboBox(ComboBoxModel aModel) { super(); setModel(aModel); init(); @@ -189,9 +191,9 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @param items an array of objects to insert into the combo box * @see DefaultComboBoxModel */ - public JComboBox(final Object items[]) { + public JComboBox(E[] items) { super(); - setModel(new DefaultComboBoxModel(items)); + setModel(new DefaultComboBoxModel(items)); init(); } @@ -203,9 +205,9 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @param items an array of vectors to insert into the combo box * @see DefaultComboBoxModel */ - public JComboBox(Vector items) { + public JComboBox(Vector items) { super(); - setModel(new DefaultComboBoxModel(items)); + setModel(new DefaultComboBoxModel(items)); init(); } @@ -219,7 +221,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { */ public JComboBox() { super(); - setModel(new DefaultComboBoxModel()); + setModel(new DefaultComboBoxModel()); init(); } @@ -263,7 +265,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { public void updateUI() { setUI((ComboBoxUI)UIManager.getUI(this)); - ListCellRenderer renderer = getRenderer(); + ListCellRenderer renderer = getRenderer(); if (renderer instanceof Component) { SwingUtilities.updateComponentTreeUI((Component)renderer); } @@ -302,8 +304,8 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * bound: true * description: Model that the combo box uses to get data to display. */ - public void setModel(ComboBoxModel aModel) { - ComboBoxModel oldModel = dataModel; + public void setModel(ComboBoxModel aModel) { + ComboBoxModel oldModel = dataModel; if (oldModel != null) { oldModel.removeListDataListener(this); } @@ -322,7 +324,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @return the ComboBoxModel that provides the displayed * list of items */ - public ComboBoxModel getModel() { + public ComboBoxModel getModel() { return dataModel; } @@ -461,8 +463,8 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * expert: true * description: The renderer that paints the item selected in the list. */ - public void setRenderer(ListCellRenderer aRenderer) { - ListCellRenderer oldRenderer = renderer; + public void setRenderer(ListCellRenderer aRenderer) { + ListCellRenderer oldRenderer = renderer; renderer = aRenderer; firePropertyChange( "renderer", oldRenderer, renderer ); invalidate(); @@ -475,7 +477,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @return the ListCellRenderer that displays * the selected item. */ - public ListCellRenderer getRenderer() { + public ListCellRenderer getRenderer() { return renderer; } @@ -558,7 +560,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { // will be rejected. boolean found = false; for (int i = 0; i < dataModel.getSize(); i++) { - Object element = dataModel.getElementAt(i); + E element = dataModel.getElementAt(i); if (anObject.equals(element)) { found = true; objectToSelect = element; @@ -640,7 +642,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { public int getSelectedIndex() { Object sObject = dataModel.getSelectedItem(); int i,c; - Object obj; + E obj; for ( i=0,c=dataModel.getSize();i * - * @param anObject the Object to add to the list + * @param item the item to add to the list * @see MutableComboBoxModel */ - public void addItem(Object anObject) { + public void addItem(E item) { checkMutableComboBoxModel(); - ((MutableComboBoxModel)dataModel).addElement(anObject); + ((MutableComboBoxModel)dataModel).addElement(item); } /** @@ -721,14 +723,14 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * This method works only if the JComboBox uses a * mutable data model. * - * @param anObject the Object to add to the list + * @param item the item to add to the list * @param index an integer specifying the position at which * to add the item * @see MutableComboBoxModel */ - public void insertItemAt(Object anObject, int index) { + public void insertItemAt(E item, int index) { checkMutableComboBoxModel(); - ((MutableComboBoxModel)dataModel).insertElementAt(anObject,index); + ((MutableComboBoxModel)dataModel).insertElementAt(item,index); } /** @@ -756,7 +758,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { */ public void removeItemAt(int anIndex) { checkMutableComboBoxModel(); - ((MutableComboBoxModel)dataModel).removeElementAt( anIndex ); + ((MutableComboBoxModel)dataModel).removeElementAt( anIndex ); } /** @@ -764,7 +766,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { */ public void removeAllItems() { checkMutableComboBoxModel(); - MutableComboBoxModel model = (MutableComboBoxModel)dataModel; + MutableComboBoxModel model = (MutableComboBoxModel)dataModel; int size = model.getSize(); if ( model instanceof DefaultComboBoxModel ) { @@ -772,7 +774,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { } else { for ( int i = 0; i < size; ++i ) { - Object element = model.getElementAt( 0 ); + E element = model.getElementAt( 0 ); model.removeElement( element ); } } @@ -1188,11 +1190,11 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { private static class ComboBoxActionPropertyChangeListener - extends ActionPropertyChangeListener { - ComboBoxActionPropertyChangeListener(JComboBox b, Action a) { + extends ActionPropertyChangeListener> { + ComboBoxActionPropertyChangeListener(JComboBox b, Action a) { super(b, a); } - protected void actionPropertyChanged(JComboBox cb, + protected void actionPropertyChanged(JComboBox cb, Action action, PropertyChangeEvent e) { if (AbstractAction.shouldReconfigure(e)) { @@ -1454,10 +1456,10 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * * @param index an integer indicating the list position, where the first * item starts at zero - * @return the Object at that list position; or + * @return the item at that list position; or * null if out of range */ - public Object getItemAt(int index) { + public E getItemAt(int index) { return dataModel.getElementAt(index); } diff --git a/jdk/src/share/classes/javax/swing/MutableComboBoxModel.java b/jdk/src/share/classes/javax/swing/MutableComboBoxModel.java index 03b75631af9..90f9474a7ea 100644 --- a/jdk/src/share/classes/javax/swing/MutableComboBoxModel.java +++ b/jdk/src/share/classes/javax/swing/MutableComboBoxModel.java @@ -27,19 +27,21 @@ package javax.swing; /** * A mutable version of ComboBoxModel. * + * @param the type of the elements of this model + * * @author Tom Santos */ -public interface MutableComboBoxModel extends ComboBoxModel { +public interface MutableComboBoxModel extends ComboBoxModel { /** * Adds an item at the end of the model. The implementation of this method * should notify all registered ListDataListeners that the * item has been added. * - * @param obj the Object to be added + * @param item the item to be added */ - public void addElement( Object obj ); + public void addElement( E item ); /** * Removes an item from the model. The implementation of this method should @@ -55,17 +57,17 @@ public interface MutableComboBoxModel extends ComboBoxModel { * should notify all registered ListDataListeners that the * item has been added. * - * @param obj the Object to be added + * @param item the item to be added * @param index location to add the object */ - public void insertElementAt( Object obj, int index ); + public void insertElementAt( E item, int index ); /** * Removes an item at a specific index. The implementation of this method * should notify all registered ListDataListeners that the * item has been removed. * - * @param index location of object to be removed + * @param index location of the item to be removed */ public void removeElementAt( int index ); } diff --git a/jdk/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java b/jdk/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java index f34b8d1684c..23252d77a58 100644 --- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java @@ -40,7 +40,7 @@ import sun.awt.shell.ShellFolder; * * @author Jeff Dinkins */ -public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener { +public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener { private JFileChooser filechooser = null; // PENDING(jeff) pick the size more sensibly diff --git a/jdk/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java b/jdk/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java index e36a849dce4..b3d38477bee 100644 --- a/jdk/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/jdk/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -906,7 +906,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI { /** * Data model for a type-face selection combo-box. */ - protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { + protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { Vector directories = new Vector(); int[] depths = null; File selectedDirectory = null; @@ -1063,7 +1063,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI { /** * Data model for a type-face selection combo-box. */ - protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener { + protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener { protected FileFilter[] filters; protected FilterComboBoxModel() { super(); diff --git a/jdk/test/javax/swing/JComboBox/7031551/bug7031551.java b/jdk/test/javax/swing/JComboBox/7031551/bug7031551.java new file mode 100644 index 00000000000..e246805c628 --- /dev/null +++ b/jdk/test/javax/swing/JComboBox/7031551/bug7031551.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* @test + * @bug 7031551 + * @summary Generics: JComboBox + * @author Pavel Porvatov + */ + +import javax.swing.*; +import java.util.Vector; + +public class bug7031551 { + + private static final String TEST_ELEMENT1 = "Test1"; + private static final String TEST_ELEMENT2 = "Test2"; + private static final String TEST_ELEMENT3 = "Test3"; + + /** + * @param args the command line arguments + */ + @SuppressWarnings("unchecked") + public static void main(String[] args) { + testRawSignatures(); + testGenericSignatures(); + } + + @SuppressWarnings("unchecked") + private static void testRawSignatures() { + // Test JComboBox + ComboBoxModel rawTestModel = new DefaultComboBoxModel(); + + JComboBox rawTestComboBox = new JComboBox(); + rawTestComboBox = new JComboBox(rawTestModel); + rawTestComboBox = new JComboBox(new Object[]{TEST_ELEMENT1}); + rawTestComboBox = new JComboBox(new Vector()); + + Object unused1 = rawTestComboBox.getPrototypeDisplayValue(); + rawTestComboBox.setPrototypeDisplayValue(TEST_ELEMENT1); + + ListCellRenderer unused2 = rawTestComboBox.getRenderer(); + rawTestComboBox.setRenderer(new DefaultListCellRenderer()); + + ComboBoxModel unused3 = rawTestComboBox.getModel(); + rawTestComboBox.setModel(rawTestModel); + + rawTestComboBox.addItem(TEST_ELEMENT2); + rawTestComboBox.insertItemAt(TEST_ELEMENT3, 1); + rawTestComboBox.removeItem(TEST_ELEMENT2); + assertEquals(rawTestComboBox.getItemAt(0), TEST_ELEMENT3); + rawTestComboBox.removeAllItems(); + + // Test DefaultComboBoxModel + DefaultComboBoxModel testModel = new DefaultComboBoxModel(); + testModel = new DefaultComboBoxModel(new Vector()); + testModel = new DefaultComboBoxModel(new Object[]{TEST_ELEMENT1}); + + testModel.addElement(TEST_ELEMENT2); + testModel.insertElementAt(TEST_ELEMENT3, 1); + assertEquals(testModel.getElementAt(2), TEST_ELEMENT2); + } + + private static void testGenericSignatures() { + // Test JComboBox + ComboBoxModel stringTestModel = new DefaultComboBoxModel(); + + JComboBox stringTestComboBox = new JComboBox(); + stringTestComboBox = new JComboBox(stringTestModel); + stringTestComboBox = new JComboBox(new String[]{TEST_ELEMENT1}); + stringTestComboBox = new JComboBox(new Vector()); + + String unused1 = stringTestComboBox.getPrototypeDisplayValue(); + stringTestComboBox.setPrototypeDisplayValue(TEST_ELEMENT1); + + ListCellRenderer unused2 = stringTestComboBox.getRenderer(); + stringTestComboBox.setRenderer(new DefaultListCellRenderer()); + + ComboBoxModel unused3 = stringTestComboBox.getModel(); + stringTestComboBox.setModel(stringTestModel); + + stringTestComboBox.addItem(TEST_ELEMENT2); + stringTestComboBox.insertItemAt(TEST_ELEMENT3, 1); + stringTestComboBox.removeItem(TEST_ELEMENT2); + assertEquals(stringTestComboBox.getItemAt(0), TEST_ELEMENT3); + stringTestComboBox.removeAllItems(); + + // Test DefaultComboBoxModel + DefaultComboBoxModel testModel = new DefaultComboBoxModel(); + testModel = new DefaultComboBoxModel(new Vector()); + testModel = new DefaultComboBoxModel(new String[]{TEST_ELEMENT1}); + + testModel.addElement(TEST_ELEMENT2); + testModel.insertElementAt(TEST_ELEMENT3, 1); + assertEquals(testModel.getElementAt(2), TEST_ELEMENT2); + } + + private static void assertEquals(Object expectedObject, Object actualObject) { + if (!expectedObject.equals(actualObject)) { + throw new RuntimeException("Expected: " + expectedObject + " but was: " + actualObject); + } + } +} + From 1bde2994742f8618f268acd08b1ce1babe3ce005 Mon Sep 17 00:00:00 2001 From: Andrei Dmitriev Date: Wed, 4 May 2011 14:46:11 +0400 Subject: [PATCH 03/65] 7040577: Default implementation of Toolkit.loadSystemColors(int[]) and many others doesn't throw HE in hl env Reviewed-by: dcherepanov, denis --- jdk/src/share/classes/java/awt/Toolkit.java | 63 ++-- .../ExceptionContract/ExceptionContract.java | 336 ++++++++++++++++++ 2 files changed, 362 insertions(+), 37 deletions(-) create mode 100644 jdk/test/java/awt/Toolkit/Headless/ExceptionContract/ExceptionContract.java diff --git a/jdk/src/share/classes/java/awt/Toolkit.java b/jdk/src/share/classes/java/awt/Toolkit.java index 6e00e6afdbf..c26df86129b 100644 --- a/jdk/src/share/classes/java/awt/Toolkit.java +++ b/jdk/src/share/classes/java/awt/Toolkit.java @@ -466,10 +466,7 @@ public abstract class Toolkit { */ protected void loadSystemColors(int[] systemColors) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } - + GraphicsEnvironment.checkHeadless(); } /** @@ -504,10 +501,7 @@ public abstract class Toolkit { */ public void setDynamicLayout(boolean dynamic) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } - + GraphicsEnvironment.checkHeadless(); } /** @@ -531,9 +525,8 @@ public abstract class Toolkit { */ protected boolean isDynamicLayoutSet() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().isDynamicLayoutSet(); } else { @@ -569,9 +562,8 @@ public abstract class Toolkit { */ public boolean isDynamicLayoutActive() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().isDynamicLayoutActive(); } else { @@ -615,9 +607,7 @@ public abstract class Toolkit { */ public Insets getScreenInsets(GraphicsConfiguration gc) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().getScreenInsets(gc); } else { @@ -1359,9 +1349,8 @@ public abstract class Toolkit { * @since 1.4 */ public Clipboard getSystemSelection() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().getSystemSelection(); } else { @@ -1391,9 +1380,7 @@ public abstract class Toolkit { * @since JDK1.1 */ public int getMenuShortcutKeyMask() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); return Event.CTRL_MASK; } @@ -1418,7 +1405,10 @@ public abstract class Toolkit { * @since 1.3 */ public boolean getLockingKeyState(int keyCode) - throws UnsupportedOperationException { + throws UnsupportedOperationException + { + GraphicsEnvironment.checkHeadless(); + if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK || keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) { throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState"); @@ -1449,7 +1439,10 @@ public abstract class Toolkit { * @since 1.3 */ public void setLockingKeyState(int keyCode, boolean on) - throws UnsupportedOperationException { + throws UnsupportedOperationException + { + GraphicsEnvironment.checkHeadless(); + if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK || keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) { throw new IllegalArgumentException("invalid key for Toolkit.setLockingKeyState"); @@ -1523,9 +1516,8 @@ public abstract class Toolkit { */ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + // Override to implement custom cursor support. if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit(). @@ -1553,9 +1545,8 @@ public abstract class Toolkit { * @since 1.2 */ public int getMaximumCursorColors() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + // Override to implement custom cursor support. if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().getMaximumCursorColors(); @@ -1605,9 +1596,8 @@ public abstract class Toolkit { public boolean isFrameStateSupported(int state) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit(). isFrameStateSupported(state); @@ -2614,9 +2604,8 @@ public abstract class Toolkit { * @since 1.7 */ public boolean areExtraMouseButtonsEnabled() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + return Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled(); } } diff --git a/jdk/test/java/awt/Toolkit/Headless/ExceptionContract/ExceptionContract.java b/jdk/test/java/awt/Toolkit/Headless/ExceptionContract/ExceptionContract.java new file mode 100644 index 00000000000..e6835e69e92 --- /dev/null +++ b/jdk/test/java/awt/Toolkit/Headless/ExceptionContract/ExceptionContract.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* + @test + @bug 7040577 + @library ../../../regtesthelpers + @build Sysout + @summary Default implementation of Toolkit.loadSystemColors(int[]) and many others doesn't throw HE in hl env + @author andrei dmitriev: area=awt.headless + @run main/othervm -Djava.awt.headless=true ExceptionContract +*/ + +import java.awt.*; +import java.util.Properties; +import test.java.awt.regtesthelpers.Sysout; + +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.*; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.font.TextAttribute; +import java.awt.im.InputMethodHighlight; +import java.awt.image.*; +import java.awt.peer.*; +import java.net.URL; +import java.util.Map; +import java.util.Properties; + +public class ExceptionContract { + + private static boolean passed = false; + public static void main(String[] args) { + //Case1 + try{ + new _Toolkit().getLockingKeyState(1); + } catch (HeadlessException he){ + passed = true; + } + if (!passed){ + throw new RuntimeException("Tk.getLockingKeyState() didn't throw HeadlessException while in the headless mode."); + } + + passed = false; + //Case2 + try{ + new _Toolkit().setLockingKeyState(1, true); + } catch (HeadlessException he){ + passed = true; + } + if (!passed){ + throw new RuntimeException("Tk.setLockingKeyState() didn't throw HeadlessException while in the headless mode."); + } + + passed = false; + //Case3 + try{ + new _Toolkit().createCustomCursor(new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB), new Point(0,0), "Custom cursor"); + } catch (HeadlessException he){ + he.printStackTrace(); + passed = true; + } + if (!passed){ + throw new RuntimeException("Tk.createCustomCursor(args) didn't throw HeadlessException while in the headless mode."); + } + + } + + static class _Toolkit extends Toolkit { + + @Override + public Cursor createCustomCursor(Image cursor, Point hotSpot, String name) + throws IndexOutOfBoundsException, HeadlessException + { + return super.createCustomCursor(cursor, hotSpot, name); + } + + + @Override + public void setLockingKeyState(int keyCode, boolean on) throws UnsupportedOperationException { + super.setLockingKeyState(keyCode, on); + } + + @Override + public boolean getLockingKeyState(int keyCode) throws UnsupportedOperationException { + return super.getLockingKeyState(keyCode); + } + + + @Override + public void loadSystemColors(int[] systemColors) throws HeadlessException { + return; + } + + @Override + protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException { + return null; + } + + @Override + protected ButtonPeer createButton(Button target) throws HeadlessException { + return null; + } + + @Override + protected TextFieldPeer createTextField(TextField target) throws HeadlessException { + return null; + } + + @Override + protected LabelPeer createLabel(Label target) throws HeadlessException { + return null; + } + + @Override + protected ListPeer createList(List target) throws HeadlessException { + return null; + } + + @Override + protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException { + return null; + } + + @Override + protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException { + return null; + } + + @Override + protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException { + return null; + } + + @Override + protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException { + return null; + } + + @Override + protected ChoicePeer createChoice(Choice target) throws HeadlessException { + return null; + } + + @Override + protected FramePeer createFrame(Frame target) throws HeadlessException { + return null; + } + + @Override + protected CanvasPeer createCanvas(Canvas target) { + return null; + } + + @Override + protected PanelPeer createPanel(Panel target) { + return null; + } + + @Override + protected WindowPeer createWindow(Window target) throws HeadlessException { + return null; + } + + @Override + protected DialogPeer createDialog(Dialog target) throws HeadlessException { + return null; + } + + @Override + protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException { + return null; + } + + @Override + protected MenuPeer createMenu(Menu target) throws HeadlessException { + return null; + } + + @Override + protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException { + return null; + } + + @Override + protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException { + return null; + } + + @Override + protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException { + return null; + } + + @Override + protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException { + return null; + } + + @Override + protected FontPeer getFontPeer(String name, int style) { + return null; + } + + @Override + public Dimension getScreenSize() throws HeadlessException { + return null; + } + + @Override + public int getScreenResolution() throws HeadlessException { + return 0; + } + + @Override + public ColorModel getColorModel() throws HeadlessException { + return null; + } + + @Override + public String[] getFontList() { + return new String[0]; + } + + @Override + public FontMetrics getFontMetrics(Font font) { + return null; + } + + @Override + public void sync() { + + } + + @Override + public Image getImage(String filename) { + return null; + } + + @Override + public Image getImage(URL url) { + return null; + } + + @Override + public Image createImage(String filename) { + return null; + } + + @Override + public Image createImage(URL url) { + return null; + } + + @Override + public boolean prepareImage(Image image, int width, int height, ImageObserver observer) { + return false; + } + + @Override + public int checkImage(Image image, int width, int height, ImageObserver observer) { + return 0; + } + + @Override + public Image createImage(ImageProducer producer) { + return null; + } + + @Override + public Image createImage(byte[] imagedata, int imageoffset, int imagelength) { + return null; + } + + @Override + public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) { + return null; + } + + @Override + public void beep() { + + } + + @Override + public Clipboard getSystemClipboard() throws HeadlessException { + return null; + } + + @Override + protected EventQueue getSystemEventQueueImpl() { + return null; + } + + @Override + public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException { + return null; + } + + @Override + public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) { + return false; + } + + @Override + public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType modalExclusionType) { + return false; + } + + @Override + public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException { + return null; + } + } +} From 86b4b082bb8728ddabe80caef3976f41247af8c8 Mon Sep 17 00:00:00 2001 From: Andrei Dmitriev Date: Fri, 6 May 2011 16:01:17 +0400 Subject: [PATCH 04/65] 6894541: javax/swing/JTable/6788484/bug6788484.java fails w/ compilation errors Reviewed-by: alexp --- jdk/test/javax/swing/JTable/6788484/bug6788484.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/jdk/test/javax/swing/JTable/6788484/bug6788484.java b/jdk/test/javax/swing/JTable/6788484/bug6788484.java index ded6f1fb72e..392bc945042 100644 --- a/jdk/test/javax/swing/JTable/6788484/bug6788484.java +++ b/jdk/test/javax/swing/JTable/6788484/bug6788484.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2011 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 @@ -24,9 +24,17 @@ /* @test @bug 6788484 @summary NPE in DefaultTableCellHeaderRenderer.getColumnSortOrder() with null table + @compile -XDignore.symbol.file=true bug6788484.java @author Alexander Potochkin @run main bug6788484 */ + +/* + * Compile with -XDignore.symbol.file=true option as a workaround for + * specific behaviour described in 6380059 which restricts proprietary + * package loading + */ + import sun.swing.table.DefaultTableCellHeaderRenderer; import javax.swing.*; From bb2c21a0252d12dc9edef3b676a12051caf7643e Mon Sep 17 00:00:00 2001 From: Igor Veresov Date: Fri, 6 May 2011 12:12:29 -0700 Subject: [PATCH 05/65] 7042153: guarantee(x_compare_res != Constant::not_comparable) failed: incomparable constants in IfOp Handle IfOps folding properly in case of unloaded constant oop arguments Reviewed-by: kvn, never --- .../src/share/vm/c1/c1_InstructionPrinter.cpp | 13 ++++-- hotspot/src/share/vm/c1/c1_Optimizer.cpp | 28 ++++++------ .../test/compiler/7042153/Test7042153.java | 45 +++++++++++++++++++ 3 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 hotspot/test/compiler/7042153/Test7042153.java diff --git a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp index e27ce064d94..1db5a4dd1b7 100644 --- a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp +++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp @@ -132,17 +132,22 @@ void InstructionPrinter::print_object(Value obj) { if (value->is_null_object()) { output()->print("null"); } else if (!value->is_loaded()) { - output()->print("", value); + output()->print("", value); } else if (value->is_method()) { ciMethod* m = (ciMethod*)value; output()->print("", m->holder()->name()->as_utf8(), m->name()->as_utf8()); } else { - output()->print("", value->constant_encoding()); + output()->print("", value->constant_encoding()); } } else if (type->as_InstanceConstant() != NULL) { - output()->print("", type->as_InstanceConstant()->value()->constant_encoding()); + ciInstance* value = type->as_InstanceConstant()->value(); + if (value->is_loaded()) { + output()->print("", value->constant_encoding()); + } else { + output()->print("", value); + } } else if (type->as_ArrayConstant() != NULL) { - output()->print("", type->as_ArrayConstant()->value()->constant_encoding()); + output()->print("", type->as_ArrayConstant()->value()->constant_encoding()); } else if (type->as_ClassConstant() != NULL) { ciInstanceKlass* klass = type->as_ClassConstant()->value(); if (!klass->is_loaded()) { diff --git a/hotspot/src/share/vm/c1/c1_Optimizer.cpp b/hotspot/src/share/vm/c1/c1_Optimizer.cpp index f07494d379c..8d21edb7607 100644 --- a/hotspot/src/share/vm/c1/c1_Optimizer.cpp +++ b/hotspot/src/share/vm/c1/c1_Optimizer.cpp @@ -252,26 +252,28 @@ Value CE_Eliminator::make_ifop(Value x, Instruction::Condition cond, Value y, Va Constant::CompareResult t_compare_res = x_tval_const->compare(cond, y_const); Constant::CompareResult f_compare_res = x_fval_const->compare(cond, y_const); - guarantee(t_compare_res != Constant::not_comparable && f_compare_res != Constant::not_comparable, "incomparable constants in IfOp"); + // not_comparable here is a valid return in case we're comparing unloaded oop constants + if (t_compare_res != Constant::not_comparable && f_compare_res != Constant::not_comparable) { + Value new_tval = t_compare_res == Constant::cond_true ? tval : fval; + Value new_fval = f_compare_res == Constant::cond_true ? tval : fval; - Value new_tval = t_compare_res == Constant::cond_true ? tval : fval; - Value new_fval = f_compare_res == Constant::cond_true ? tval : fval; - - _ifop_count++; - if (new_tval == new_fval) { - return new_tval; - } else { - return new IfOp(x_ifop->x(), x_ifop_cond, x_ifop->y(), new_tval, new_fval); + _ifop_count++; + if (new_tval == new_fval) { + return new_tval; + } else { + return new IfOp(x_ifop->x(), x_ifop_cond, x_ifop->y(), new_tval, new_fval); + } } } } else { Constant* x_const = x->as_Constant(); if (x_const != NULL) { // x and y are constants Constant::CompareResult x_compare_res = x_const->compare(cond, y_const); - guarantee(x_compare_res != Constant::not_comparable, "incomparable constants in IfOp"); - - _ifop_count++; - return x_compare_res == Constant::cond_true ? tval : fval; + // not_comparable here is a valid return in case we're comparing unloaded oop constants + if (x_compare_res != Constant::not_comparable) { + _ifop_count++; + return x_compare_res == Constant::cond_true ? tval : fval; + } } } } diff --git a/hotspot/test/compiler/7042153/Test7042153.java b/hotspot/test/compiler/7042153/Test7042153.java new file mode 100644 index 00000000000..4319b3b2036 --- /dev/null +++ b/hotspot/test/compiler/7042153/Test7042153.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011, 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. + * + */ + +/** + * @test + * @bug 7042153 + * @summary Bad folding of IfOps with unloaded constant arguments in C1 + * + * @run main/othervm -Xcomp Test7042153 + */ + +import java.lang.reflect.*; + +public class Test7042153 { + static public class Bar { } + static public class Foo { } + + static volatile boolean z; + public static void main(String [] args) { + Class cx = Bar.class; + Class cy = Foo.class; + z = (cx == cy); + } +} From 6aeaca98d1ffc9b203029cfbc4896a413fd137da Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Fri, 6 May 2011 16:33:13 -0700 Subject: [PATCH 06/65] 6939861: JVM should handle more conversion operations Reviewed-by: twisti, jrose --- hotspot/src/cpu/x86/vm/assembler_x86.hpp | 28 +- hotspot/src/cpu/x86/vm/frame_x86.cpp | 137 +- hotspot/src/cpu/x86/vm/frame_x86.hpp | 4 + hotspot/src/cpu/x86/vm/frame_x86.inline.hpp | 1 + hotspot/src/cpu/x86/vm/interpreter_x86.hpp | 4 +- hotspot/src/cpu/x86/vm/methodHandles_x86.cpp | 1790 ++++++++++++++--- hotspot/src/cpu/x86/vm/methodHandles_x86.hpp | 292 +++ .../src/cpu/x86/vm/sharedRuntime_x86_32.cpp | 27 + .../src/cpu/x86/vm/sharedRuntime_x86_64.cpp | 28 + .../src/cpu/x86/vm/stubRoutines_x86_32.hpp | 2 +- .../src/cpu/x86/vm/stubRoutines_x86_64.hpp | 2 +- .../src/share/vm/classfile/javaClasses.cpp | 24 + .../src/share/vm/classfile/javaClasses.hpp | 14 +- .../share/vm/classfile/systemDictionary.cpp | 9 +- hotspot/src/share/vm/classfile/vmSymbols.hpp | 2 + hotspot/src/share/vm/code/codeBlob.cpp | 183 +- hotspot/src/share/vm/code/codeBlob.hpp | 76 +- hotspot/src/share/vm/code/codeCache.cpp | 4 + .../src/share/vm/compiler/disassembler.cpp | 18 +- hotspot/src/share/vm/prims/jvmtiTagMap.cpp | 3 + .../src/share/vm/prims/methodHandleWalk.cpp | 25 +- hotspot/src/share/vm/prims/methodHandles.cpp | 564 +++++- hotspot/src/share/vm/prims/methodHandles.hpp | 364 +++- hotspot/src/share/vm/runtime/frame.cpp | 25 +- hotspot/src/share/vm/runtime/frame.hpp | 3 + hotspot/src/share/vm/runtime/globals.hpp | 4 + .../src/share/vm/runtime/sharedRuntime.cpp | 8 + .../src/share/vm/runtime/sharedRuntime.hpp | 12 + hotspot/src/share/vm/services/heapDumper.cpp | 3 + 29 files changed, 3011 insertions(+), 645 deletions(-) create mode 100644 hotspot/src/cpu/x86/vm/methodHandles_x86.hpp diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp index 34e8ed9c87f..c36709a33df 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp @@ -234,6 +234,20 @@ class Address VALUE_OBJ_CLASS_SPEC { a._disp += disp; return a; } + Address plus_disp(RegisterOrConstant disp, ScaleFactor scale = times_1) const { + Address a = (*this); + a._disp += disp.constant_or_zero() * scale_size(scale); + if (disp.is_register()) { + assert(!a.index()->is_valid(), "competing indexes"); + a._index = disp.as_register(); + a._scale = scale; + } + return a; + } + bool is_same_address(Address a) const { + // disregard _rspec + return _base == a._base && _disp == a._disp && _index == a._index && _scale == a._scale; + } // The following two overloads are used in connection with the // ByteSize type (see sizes.hpp). They simplify the use of @@ -2029,6 +2043,10 @@ class MacroAssembler: public Assembler { void addptr(Register dst, Address src) { LP64_ONLY(addq(dst, src)) NOT_LP64(addl(dst, src)); } void addptr(Register dst, int32_t src); void addptr(Register dst, Register src); + void addptr(Register dst, RegisterOrConstant src) { + if (src.is_constant()) addptr(dst, (int) src.as_constant()); + else addptr(dst, src.as_register()); + } void andptr(Register dst, int32_t src); void andptr(Register src1, Register src2) { LP64_ONLY(andq(src1, src2)) NOT_LP64(andl(src1, src2)) ; } @@ -2090,7 +2108,10 @@ class MacroAssembler: public Assembler { void subptr(Register dst, Address src) { LP64_ONLY(subq(dst, src)) NOT_LP64(subl(dst, src)); } void subptr(Register dst, int32_t src); void subptr(Register dst, Register src); - + void subptr(Register dst, RegisterOrConstant src) { + if (src.is_constant()) subptr(dst, (int) src.as_constant()); + else subptr(dst, src.as_register()); + } void sbbptr(Address dst, int32_t src) { LP64_ONLY(sbbq(dst, src)) NOT_LP64(sbbl(dst, src)); } void sbbptr(Register dst, int32_t src) { LP64_ONLY(sbbq(dst, src)) NOT_LP64(sbbl(dst, src)); } @@ -2288,6 +2309,11 @@ public: void movptr(Address dst, Register src); + void movptr(Register dst, RegisterOrConstant src) { + if (src.is_constant()) movptr(dst, src.as_constant()); + else movptr(dst, src.as_register()); + } + #ifdef _LP64 // Generally the next two are only used for moving NULL // Although there are situations in initializing the mark word where diff --git a/hotspot/src/cpu/x86/vm/frame_x86.cpp b/hotspot/src/cpu/x86/vm/frame_x86.cpp index 8709a0e8698..cb5dded426a 100644 --- a/hotspot/src/cpu/x86/vm/frame_x86.cpp +++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp @@ -339,7 +339,6 @@ frame frame::sender_for_entry_frame(RegisterMap* map) const { return fr; } - //------------------------------------------------------------------------------ // frame::verify_deopt_original_pc // @@ -361,6 +360,55 @@ void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp, bool } #endif +//------------------------------------------------------------------------------ +// frame::adjust_unextended_sp +void frame::adjust_unextended_sp() { + // If we are returning to a compiled MethodHandle call site, the + // saved_fp will in fact be a saved value of the unextended SP. The + // simplest way to tell whether we are returning to such a call site + // is as follows: + + nmethod* sender_nm = (_cb == NULL) ? NULL : _cb->as_nmethod_or_null(); + if (sender_nm != NULL) { + // If the sender PC is a deoptimization point, get the original + // PC. For MethodHandle call site the unextended_sp is stored in + // saved_fp. + if (sender_nm->is_deopt_mh_entry(_pc)) { + DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, _fp)); + _unextended_sp = _fp; + } + else if (sender_nm->is_deopt_entry(_pc)) { + DEBUG_ONLY(verify_deopt_original_pc(sender_nm, _unextended_sp)); + } + else if (sender_nm->is_method_handle_return(_pc)) { + _unextended_sp = _fp; + } + } +} + +//------------------------------------------------------------------------------ +// frame::update_map_with_saved_link +void frame::update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr) { + // The interpreter and compiler(s) always save EBP/RBP in a known + // location on entry. We must record where that location is + // so this if EBP/RBP was live on callout from c2 we can find + // the saved copy no matter what it called. + + // Since the interpreter always saves EBP/RBP if we record where it is then + // we don't have to always save EBP/RBP on entry and exit to c2 compiled + // code, on entry will be enough. + map->set_location(rbp->as_VMReg(), (address) link_addr); +#ifdef AMD64 + // this is weird "H" ought to be at a higher address however the + // oopMaps seems to have the "H" regs at the same address and the + // vanilla register. + // XXXX make this go away + if (true) { + map->set_location(rbp->as_VMReg()->next(), (address) link_addr); + } +#endif // AMD64 +} + //------------------------------------------------------------------------------ // frame::sender_for_interpreter_frame @@ -372,54 +420,13 @@ frame frame::sender_for_interpreter_frame(RegisterMap* map) const { // This is the sp before any possible extension (adapter/locals). intptr_t* unextended_sp = interpreter_frame_sender_sp(); - // Stored FP. - intptr_t* saved_fp = link(); - - address sender_pc = this->sender_pc(); - CodeBlob* sender_cb = CodeCache::find_blob_unsafe(sender_pc); - assert(sender_cb, "sanity"); - nmethod* sender_nm = sender_cb->as_nmethod_or_null(); - - if (sender_nm != NULL) { - // If the sender PC is a deoptimization point, get the original - // PC. For MethodHandle call site the unextended_sp is stored in - // saved_fp. - if (sender_nm->is_deopt_mh_entry(sender_pc)) { - DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, saved_fp)); - unextended_sp = saved_fp; - } - else if (sender_nm->is_deopt_entry(sender_pc)) { - DEBUG_ONLY(verify_deopt_original_pc(sender_nm, unextended_sp)); - } - else if (sender_nm->is_method_handle_return(sender_pc)) { - unextended_sp = saved_fp; - } - } - - // The interpreter and compiler(s) always save EBP/RBP in a known - // location on entry. We must record where that location is - // so this if EBP/RBP was live on callout from c2 we can find - // the saved copy no matter what it called. - - // Since the interpreter always saves EBP/RBP if we record where it is then - // we don't have to always save EBP/RBP on entry and exit to c2 compiled - // code, on entry will be enough. #ifdef COMPILER2 if (map->update_map()) { - map->set_location(rbp->as_VMReg(), (address) addr_at(link_offset)); -#ifdef AMD64 - // this is weird "H" ought to be at a higher address however the - // oopMaps seems to have the "H" regs at the same address and the - // vanilla register. - // XXXX make this go away - if (true) { - map->set_location(rbp->as_VMReg()->next(), (address)addr_at(link_offset)); - } -#endif // AMD64 + update_map_with_saved_link(map, (intptr_t**) addr_at(link_offset)); } #endif // COMPILER2 - return frame(sender_sp, unextended_sp, saved_fp, sender_pc); + return frame(sender_sp, unextended_sp, link(), sender_pc()); } @@ -427,6 +434,7 @@ frame frame::sender_for_interpreter_frame(RegisterMap* map) const { // frame::sender_for_compiled_frame frame frame::sender_for_compiled_frame(RegisterMap* map) const { assert(map != NULL, "map must be set"); + assert(!is_ricochet_frame(), "caller must handle this"); // frame owned by optimizing compiler assert(_cb->frame_size() >= 0, "must have non-zero frame size"); @@ -438,31 +446,7 @@ frame frame::sender_for_compiled_frame(RegisterMap* map) const { // This is the saved value of EBP which may or may not really be an FP. // It is only an FP if the sender is an interpreter frame (or C1?). - intptr_t* saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset); - - // If we are returning to a compiled MethodHandle call site, the - // saved_fp will in fact be a saved value of the unextended SP. The - // simplest way to tell whether we are returning to such a call site - // is as follows: - CodeBlob* sender_cb = CodeCache::find_blob_unsafe(sender_pc); - assert(sender_cb, "sanity"); - nmethod* sender_nm = sender_cb->as_nmethod_or_null(); - - if (sender_nm != NULL) { - // If the sender PC is a deoptimization point, get the original - // PC. For MethodHandle call site the unextended_sp is stored in - // saved_fp. - if (sender_nm->is_deopt_mh_entry(sender_pc)) { - DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, saved_fp)); - unextended_sp = saved_fp; - } - else if (sender_nm->is_deopt_entry(sender_pc)) { - DEBUG_ONLY(verify_deopt_original_pc(sender_nm, unextended_sp)); - } - else if (sender_nm->is_method_handle_return(sender_pc)) { - unextended_sp = saved_fp; - } - } + intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset); if (map->update_map()) { // Tell GC to use argument oopmaps for some runtime stubs that need it. @@ -472,23 +456,15 @@ frame frame::sender_for_compiled_frame(RegisterMap* map) const { if (_cb->oop_maps() != NULL) { OopMapSet::update_register_map(this, map); } + // Since the prolog does the save and restore of EBP there is no oopmap // for it so we must fill in its location as if there was an oopmap entry // since if our caller was compiled code there could be live jvm state in it. - map->set_location(rbp->as_VMReg(), (address) (sender_sp - frame::sender_sp_offset)); -#ifdef AMD64 - // this is weird "H" ought to be at a higher address however the - // oopMaps seems to have the "H" regs at the same address and the - // vanilla register. - // XXXX make this go away - if (true) { - map->set_location(rbp->as_VMReg()->next(), (address) (sender_sp - frame::sender_sp_offset)); - } -#endif // AMD64 + update_map_with_saved_link(map, saved_fp_addr); } assert(sender_sp != sp(), "must have changed"); - return frame(sender_sp, unextended_sp, saved_fp, sender_pc); + return frame(sender_sp, unextended_sp, *saved_fp_addr, sender_pc); } @@ -502,6 +478,7 @@ frame frame::sender(RegisterMap* map) const { if (is_entry_frame()) return sender_for_entry_frame(map); if (is_interpreted_frame()) return sender_for_interpreter_frame(map); assert(_cb == CodeCache::find_blob(pc()),"Must be the same"); + if (is_ricochet_frame()) return sender_for_ricochet_frame(map); if (_cb != NULL) { return sender_for_compiled_frame(map); diff --git a/hotspot/src/cpu/x86/vm/frame_x86.hpp b/hotspot/src/cpu/x86/vm/frame_x86.hpp index ea92dc518cc..a307a340428 100644 --- a/hotspot/src/cpu/x86/vm/frame_x86.hpp +++ b/hotspot/src/cpu/x86/vm/frame_x86.hpp @@ -164,6 +164,7 @@ // original sp we use that convention. intptr_t* _unextended_sp; + void adjust_unextended_sp(); intptr_t* ptr_at_addr(int offset) const { return (intptr_t*) addr_at(offset); @@ -197,6 +198,9 @@ // expression stack tos if we are nested in a java call intptr_t* interpreter_frame_last_sp() const; + // helper to update a map with callee-saved RBP + static void update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr); + #ifndef CC_INTERP // deoptimization support void interpreter_frame_set_last_sp(intptr_t* sp); diff --git a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp index bb9ac15cb31..d093facdc15 100644 --- a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp +++ b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp @@ -62,6 +62,7 @@ inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address _pc = pc; assert(pc != NULL, "no pc?"); _cb = CodeCache::find_blob(pc); + adjust_unextended_sp(); address original_pc = nmethod::get_deopt_original_pc(this); if (original_pc != NULL) { diff --git a/hotspot/src/cpu/x86/vm/interpreter_x86.hpp b/hotspot/src/cpu/x86/vm/interpreter_x86.hpp index f3ff867b893..8a6169c0ca4 100644 --- a/hotspot/src/cpu/x86/vm/interpreter_x86.hpp +++ b/hotspot/src/cpu/x86/vm/interpreter_x86.hpp @@ -26,7 +26,9 @@ #define CPU_X86_VM_INTERPRETER_X86_HPP public: - static Address::ScaleFactor stackElementScale() { return Address::times_4; } + static Address::ScaleFactor stackElementScale() { + return NOT_LP64(Address::times_4) LP64_ONLY(Address::times_8); + } // Offset from rsp (which points to the last stack element) static int expr_offset_in_bytes(int i) { return stackElementSize * i; } diff --git a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp index 42a57c1322d..8f73dc13d8d 100644 --- a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp +++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp @@ -69,23 +69,475 @@ MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _mas return me; } +// stack walking support + +frame MethodHandles::ricochet_frame_sender(const frame& fr, RegisterMap *map) { + RicochetFrame* f = RicochetFrame::from_frame(fr); + if (map->update_map()) + frame::update_map_with_saved_link(map, &f->_sender_link); + return frame(f->extended_sender_sp(), f->exact_sender_sp(), f->sender_link(), f->sender_pc()); +} + +void MethodHandles::ricochet_frame_oops_do(const frame& fr, OopClosure* blk, const RegisterMap* reg_map) { + RicochetFrame* f = RicochetFrame::from_frame(fr); + + // pick up the argument type descriptor: + Thread* thread = Thread::current(); + Handle cookie(thread, f->compute_saved_args_layout(true, true)); + + // process fixed part + blk->do_oop((oop*)f->saved_target_addr()); + blk->do_oop((oop*)f->saved_args_layout_addr()); + + // process variable arguments: + if (cookie.is_null()) return; // no arguments to describe + + // the cookie is actually the invokeExact method for my target + // his argument signature is what I'm interested in + assert(cookie->is_method(), ""); + methodHandle invoker(thread, methodOop(cookie())); + assert(invoker->name() == vmSymbols::invokeExact_name(), "must be this kind of method"); + assert(!invoker->is_static(), "must have MH argument"); + int slot_count = invoker->size_of_parameters(); + assert(slot_count >= 1, "must include 'this'"); + intptr_t* base = f->saved_args_base(); + intptr_t* retval = NULL; + if (f->has_return_value_slot()) + retval = f->return_value_slot_addr(); + int slot_num = slot_count; + intptr_t* loc = &base[slot_num -= 1]; + //blk->do_oop((oop*) loc); // original target, which is irrelevant + int arg_num = 0; + for (SignatureStream ss(invoker->signature()); !ss.is_done(); ss.next()) { + if (ss.at_return_type()) continue; + BasicType ptype = ss.type(); + if (ptype == T_ARRAY) ptype = T_OBJECT; // fold all refs to T_OBJECT + assert(ptype >= T_BOOLEAN && ptype <= T_OBJECT, "not array or void"); + loc = &base[slot_num -= type2size[ptype]]; + bool is_oop = (ptype == T_OBJECT && loc != retval); + if (is_oop) blk->do_oop((oop*)loc); + arg_num += 1; + } + assert(slot_num == 0, "must have processed all the arguments"); +} + +oop MethodHandles::RicochetFrame::compute_saved_args_layout(bool read_cache, bool write_cache) { + oop cookie = NULL; + if (read_cache) { + cookie = saved_args_layout(); + if (cookie != NULL) return cookie; + } + oop target = saved_target(); + oop mtype = java_lang_invoke_MethodHandle::type(target); + oop mtform = java_lang_invoke_MethodType::form(mtype); + cookie = java_lang_invoke_MethodTypeForm::vmlayout(mtform); + if (write_cache) { + (*saved_args_layout_addr()) = cookie; + } + return cookie; +} + +void MethodHandles::RicochetFrame::generate_ricochet_blob(MacroAssembler* _masm, + // output params: + int* frame_size_in_words, + int* bounce_offset, + int* exception_offset) { + (*frame_size_in_words) = RicochetFrame::frame_size_in_bytes() / wordSize; + + address start = __ pc(); + #ifdef ASSERT -static void verify_argslot(MacroAssembler* _masm, Register argslot_reg, - const char* error_message) { + __ hlt(); __ hlt(); __ hlt(); + // here's a hint of something special: + __ push(MAGIC_NUMBER_1); + __ push(MAGIC_NUMBER_2); +#endif //ASSERT + __ hlt(); // not reached + + // A return PC has just been popped from the stack. + // Return values are in registers. + // The ebp points into the RicochetFrame, which contains + // a cleanup continuation we must return to. + + (*bounce_offset) = __ pc() - start; + BLOCK_COMMENT("ricochet_blob.bounce"); + + if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm); + trace_method_handle(_masm, "ricochet_blob.bounce"); + + __ jmp(frame_address(continuation_offset_in_bytes())); + __ hlt(); + DEBUG_ONLY(__ push(MAGIC_NUMBER_2)); + + (*exception_offset) = __ pc() - start; + BLOCK_COMMENT("ricochet_blob.exception"); + + // compare this to Interpreter::rethrow_exception_entry, which is parallel code + // for example, see TemplateInterpreterGenerator::generate_throw_exception + // Live registers in: + // rax: exception + // rdx: return address/pc that threw exception (ignored, always equal to bounce addr) + __ verify_oop(rax); + + // no need to empty_FPU_stack or reinit_heapbase, since caller frame will do the same if needed + + // Take down the frame. + + // Cf. InterpreterMacroAssembler::remove_activation. + leave_ricochet_frame(_masm, /*rcx_recv=*/ noreg, + saved_last_sp_register(), + /*sender_pc_reg=*/ rdx); + + // In between activations - previous activation type unknown yet + // compute continuation point - the continuation point expects the + // following registers set up: + // + // rax: exception + // rdx: return address/pc that threw exception + // rsp: expression stack of caller + // rbp: ebp of caller + __ push(rax); // save exception + __ push(rdx); // save return address + Register thread_reg = LP64_ONLY(r15_thread) NOT_LP64(rdi); + NOT_LP64(__ get_thread(thread_reg)); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, + SharedRuntime::exception_handler_for_return_address), + thread_reg, rdx); + __ mov(rbx, rax); // save exception handler + __ pop(rdx); // restore return address + __ pop(rax); // restore exception + __ jmp(rbx); // jump to exception + // handler of caller +} + +void MethodHandles::RicochetFrame::enter_ricochet_frame(MacroAssembler* _masm, + Register rcx_recv, + Register rax_argv, + address return_handler, + Register rbx_temp) { + const Register saved_last_sp = saved_last_sp_register(); + Address rcx_mh_vmtarget( rcx_recv, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes() ); + Address rcx_amh_conversion( rcx_recv, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes() ); + + // Push the RicochetFrame a word at a time. + // This creates something similar to an interpreter frame. + // Cf. TemplateInterpreterGenerator::generate_fixed_frame. + BLOCK_COMMENT("push RicochetFrame {"); + DEBUG_ONLY(int rfo = (int) sizeof(RicochetFrame)); + assert((rfo -= wordSize) == RicochetFrame::sender_pc_offset_in_bytes(), ""); +#define RF_FIELD(push_value, name) \ + { push_value; \ + assert((rfo -= wordSize) == RicochetFrame::name##_offset_in_bytes(), ""); } + RF_FIELD(__ push(rbp), sender_link); + RF_FIELD(__ push(saved_last_sp), exact_sender_sp); // rsi/r13 + RF_FIELD(__ pushptr(rcx_amh_conversion), conversion); + RF_FIELD(__ push(rax_argv), saved_args_base); // can be updated if args are shifted + RF_FIELD(__ push((int32_t) NULL_WORD), saved_args_layout); // cache for GC layout cookie + if (UseCompressedOops) { + __ load_heap_oop(rbx_temp, rcx_mh_vmtarget); + RF_FIELD(__ push(rbx_temp), saved_target); + } else { + RF_FIELD(__ pushptr(rcx_mh_vmtarget), saved_target); + } + __ lea(rbx_temp, ExternalAddress(return_handler)); + RF_FIELD(__ push(rbx_temp), continuation); +#undef RF_FIELD + assert(rfo == 0, "fully initialized the RicochetFrame"); + // compute new frame pointer: + __ lea(rbp, Address(rsp, RicochetFrame::sender_link_offset_in_bytes())); + // Push guard word #1 in debug mode. + DEBUG_ONLY(__ push((int32_t) RicochetFrame::MAGIC_NUMBER_1)); + // For debugging, leave behind an indication of which stub built this frame. + DEBUG_ONLY({ Label L; __ call(L, relocInfo::none); __ bind(L); }); + BLOCK_COMMENT("} RicochetFrame"); +} + +void MethodHandles::RicochetFrame::leave_ricochet_frame(MacroAssembler* _masm, + Register rcx_recv, + Register new_sp_reg, + Register sender_pc_reg) { + assert_different_registers(rcx_recv, new_sp_reg, sender_pc_reg); + const Register saved_last_sp = saved_last_sp_register(); + // Take down the frame. + // Cf. InterpreterMacroAssembler::remove_activation. + BLOCK_COMMENT("end_ricochet_frame {"); + // TO DO: If (exact_sender_sp - extended_sender_sp) > THRESH, compact the frame down. + // This will keep stack in bounds even with unlimited tailcalls, each with an adapter. + if (rcx_recv->is_valid()) + __ movptr(rcx_recv, RicochetFrame::frame_address(RicochetFrame::saved_target_offset_in_bytes())); + __ movptr(sender_pc_reg, RicochetFrame::frame_address(RicochetFrame::sender_pc_offset_in_bytes())); + __ movptr(saved_last_sp, RicochetFrame::frame_address(RicochetFrame::exact_sender_sp_offset_in_bytes())); + __ movptr(rbp, RicochetFrame::frame_address(RicochetFrame::sender_link_offset_in_bytes())); + __ mov(rsp, new_sp_reg); + BLOCK_COMMENT("} end_ricochet_frame"); +} + +// Emit code to verify that RBP is pointing at a valid ricochet frame. +#ifdef ASSERT +enum { + ARG_LIMIT = 255, SLOP = 4, + // use this parameter for checking for garbage stack movements: + UNREASONABLE_STACK_MOVE = (ARG_LIMIT + SLOP) + // the slop defends against false alarms due to fencepost errors +}; + +void MethodHandles::RicochetFrame::verify_clean(MacroAssembler* _masm) { + // The stack should look like this: + // ... keep1 | dest=42 | keep2 | RF | magic | handler | magic | recursive args | + // Check various invariants. + verify_offsets(); + + Register rdi_temp = rdi; + Register rcx_temp = rcx; + { __ push(rdi_temp); __ push(rcx_temp); } +#define UNPUSH_TEMPS \ + { __ pop(rcx_temp); __ pop(rdi_temp); } + + Address magic_number_1_addr = RicochetFrame::frame_address(RicochetFrame::magic_number_1_offset_in_bytes()); + Address magic_number_2_addr = RicochetFrame::frame_address(RicochetFrame::magic_number_2_offset_in_bytes()); + Address continuation_addr = RicochetFrame::frame_address(RicochetFrame::continuation_offset_in_bytes()); + Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes()); + Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes()); + + Label L_bad, L_ok; + BLOCK_COMMENT("verify_clean {"); + // Magic numbers must check out: + __ cmpptr(magic_number_1_addr, (int32_t) MAGIC_NUMBER_1); + __ jcc(Assembler::notEqual, L_bad); + __ cmpptr(magic_number_2_addr, (int32_t) MAGIC_NUMBER_2); + __ jcc(Assembler::notEqual, L_bad); + + // Arguments pointer must look reasonable: + __ movptr(rcx_temp, saved_args_base_addr); + __ cmpptr(rcx_temp, rbp); + __ jcc(Assembler::below, L_bad); + __ subptr(rcx_temp, UNREASONABLE_STACK_MOVE * Interpreter::stackElementSize); + __ cmpptr(rcx_temp, rbp); + __ jcc(Assembler::above, L_bad); + + load_conversion_dest_type(_masm, rdi_temp, conversion_addr); + __ cmpl(rdi_temp, T_VOID); + __ jcc(Assembler::equal, L_ok); + __ movptr(rcx_temp, saved_args_base_addr); + load_conversion_vminfo(_masm, rdi_temp, conversion_addr); + __ cmpptr(Address(rcx_temp, rdi_temp, Interpreter::stackElementScale()), + (int32_t) RETURN_VALUE_PLACEHOLDER); + __ jcc(Assembler::equal, L_ok); + __ BIND(L_bad); + UNPUSH_TEMPS; + __ stop("damaged ricochet frame"); + __ BIND(L_ok); + UNPUSH_TEMPS; + BLOCK_COMMENT("} verify_clean"); + +#undef UNPUSH_TEMPS + +} +#endif //ASSERT + +void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg) { + if (VerifyMethodHandles) + verify_klass(_masm, klass_reg, SystemDictionaryHandles::Class_klass(), + "AMH argument is a Class"); + __ load_heap_oop(klass_reg, Address(klass_reg, java_lang_Class::klass_offset_in_bytes())); +} + +void MethodHandles::load_conversion_vminfo(MacroAssembler* _masm, Register reg, Address conversion_field_addr) { + int bits = BitsPerByte; + int offset = (CONV_VMINFO_SHIFT / bits); + int shift = (CONV_VMINFO_SHIFT % bits); + __ load_unsigned_byte(reg, conversion_field_addr.plus_disp(offset)); + assert(CONV_VMINFO_MASK == right_n_bits(bits - shift), "else change type of previous load"); + assert(shift == 0, "no shift needed"); +} + +void MethodHandles::load_conversion_dest_type(MacroAssembler* _masm, Register reg, Address conversion_field_addr) { + int bits = BitsPerByte; + int offset = (CONV_DEST_TYPE_SHIFT / bits); + int shift = (CONV_DEST_TYPE_SHIFT % bits); + __ load_unsigned_byte(reg, conversion_field_addr.plus_disp(offset)); + assert(CONV_TYPE_MASK == right_n_bits(bits - shift), "else change type of previous load"); + __ shrl(reg, shift); + DEBUG_ONLY(int conv_type_bits = (int) exact_log2(CONV_TYPE_MASK+1)); + assert((shift + conv_type_bits) == bits, "left justified in byte"); +} + +void MethodHandles::load_stack_move(MacroAssembler* _masm, + Register rdi_stack_move, + Register rcx_amh, + bool might_be_negative) { + BLOCK_COMMENT("load_stack_move"); + Address rcx_amh_conversion(rcx_amh, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes()); + __ movl(rdi_stack_move, rcx_amh_conversion); + __ sarl(rdi_stack_move, CONV_STACK_MOVE_SHIFT); +#ifdef _LP64 + if (might_be_negative) { + // clean high bits of stack motion register (was loaded as an int) + __ movslq(rdi_stack_move, rdi_stack_move); + } +#endif //_LP64 + if (VerifyMethodHandles) { + Label L_ok, L_bad; + int32_t stack_move_limit = 0x4000; // extra-large + __ cmpptr(rdi_stack_move, stack_move_limit); + __ jcc(Assembler::greaterEqual, L_bad); + __ cmpptr(rdi_stack_move, -stack_move_limit); + __ jcc(Assembler::greater, L_ok); + __ bind(L_bad); + __ stop("load_stack_move of garbage value"); + __ BIND(L_ok); + } +} + +#ifndef PRODUCT +void MethodHandles::RicochetFrame::verify_offsets() { + // Check compatibility of this struct with the more generally used offsets of class frame: + int ebp_off = sender_link_offset_in_bytes(); // offset from struct base to local rbp value + assert(ebp_off + wordSize*frame::interpreter_frame_method_offset == saved_args_base_offset_in_bytes(), ""); + assert(ebp_off + wordSize*frame::interpreter_frame_last_sp_offset == conversion_offset_in_bytes(), ""); + assert(ebp_off + wordSize*frame::interpreter_frame_sender_sp_offset == exact_sender_sp_offset_in_bytes(), ""); + // These last two have to be exact: + assert(ebp_off + wordSize*frame::link_offset == sender_link_offset_in_bytes(), ""); + assert(ebp_off + wordSize*frame::return_addr_offset == sender_pc_offset_in_bytes(), ""); +} + +void MethodHandles::RicochetFrame::verify() const { + verify_offsets(); + assert(magic_number_1() == MAGIC_NUMBER_1, ""); + assert(magic_number_2() == MAGIC_NUMBER_2, ""); + if (!Universe::heap()->is_gc_active()) { + if (saved_args_layout() != NULL) { + assert(saved_args_layout()->is_method(), "must be valid oop"); + } + if (saved_target() != NULL) { + assert(java_lang_invoke_MethodHandle::is_instance(saved_target()), "checking frame value"); + } + } + int conv_op = adapter_conversion_op(conversion()); + assert(conv_op == java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS || + conv_op == java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS || + conv_op == java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF, + "must be a sane conversion"); + if (has_return_value_slot()) { + assert(*return_value_slot_addr() == RETURN_VALUE_PLACEHOLDER, ""); + } +} +#endif //PRODUCT + +#ifdef ASSERT +void MethodHandles::verify_argslot(MacroAssembler* _masm, + Register argslot_reg, + const char* error_message) { // Verify that argslot lies within (rsp, rbp]. Label L_ok, L_bad; - BLOCK_COMMENT("{ verify_argslot"); + BLOCK_COMMENT("verify_argslot {"); __ cmpptr(argslot_reg, rbp); __ jccb(Assembler::above, L_bad); __ cmpptr(rsp, argslot_reg); __ jccb(Assembler::below, L_ok); __ bind(L_bad); __ stop(error_message); - __ bind(L_ok); + __ BIND(L_ok); BLOCK_COMMENT("} verify_argslot"); } -#endif +void MethodHandles::verify_argslots(MacroAssembler* _masm, + RegisterOrConstant arg_slots, + Register arg_slot_base_reg, + bool negate_argslots, + const char* error_message) { + // Verify that [argslot..argslot+size) lies within (rsp, rbp). + Label L_ok, L_bad; + Register rdi_temp = rdi; + BLOCK_COMMENT("verify_argslots {"); + __ push(rdi_temp); + if (negate_argslots) { + if (arg_slots.is_constant()) { + arg_slots = -1 * arg_slots.as_constant(); + } else { + __ movptr(rdi_temp, arg_slots); + __ negptr(rdi_temp); + arg_slots = rdi_temp; + } + } + __ lea(rdi_temp, Address(arg_slot_base_reg, arg_slots, Interpreter::stackElementScale())); + __ cmpptr(rdi_temp, rbp); + __ pop(rdi_temp); + __ jcc(Assembler::above, L_bad); + __ cmpptr(rsp, arg_slot_base_reg); + __ jcc(Assembler::below, L_ok); + __ bind(L_bad); + __ stop(error_message); + __ BIND(L_ok); + BLOCK_COMMENT("} verify_argslots"); +} + +// Make sure that arg_slots has the same sign as the given direction. +// If (and only if) arg_slots is a assembly-time constant, also allow it to be zero. +void MethodHandles::verify_stack_move(MacroAssembler* _masm, + RegisterOrConstant arg_slots, int direction) { + bool allow_zero = arg_slots.is_constant(); + if (direction == 0) { direction = +1; allow_zero = true; } + assert(stack_move_unit() == -1, "else add extra checks here"); + if (arg_slots.is_register()) { + Label L_ok, L_bad; + BLOCK_COMMENT("verify_stack_move {"); + // testl(arg_slots.as_register(), -stack_move_unit() - 1); // no need + // jcc(Assembler::notZero, L_bad); + __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD); + if (direction > 0) { + __ jcc(allow_zero ? Assembler::less : Assembler::lessEqual, L_bad); + __ cmpptr(arg_slots.as_register(), (int32_t) UNREASONABLE_STACK_MOVE); + __ jcc(Assembler::less, L_ok); + } else { + __ jcc(allow_zero ? Assembler::greater : Assembler::greaterEqual, L_bad); + __ cmpptr(arg_slots.as_register(), (int32_t) -UNREASONABLE_STACK_MOVE); + __ jcc(Assembler::greater, L_ok); + } + __ bind(L_bad); + if (direction > 0) + __ stop("assert arg_slots > 0"); + else + __ stop("assert arg_slots < 0"); + __ BIND(L_ok); + BLOCK_COMMENT("} verify_stack_move"); + } else { + intptr_t size = arg_slots.as_constant(); + if (direction < 0) size = -size; + assert(size >= 0, "correct direction of constant move"); + assert(size < UNREASONABLE_STACK_MOVE, "reasonable size of constant move"); + } +} + +void MethodHandles::verify_klass(MacroAssembler* _masm, + Register obj, KlassHandle klass, + const char* error_message) { + oop* klass_addr = klass.raw_value(); + assert(klass_addr >= SystemDictionaryHandles::Object_klass().raw_value() && + klass_addr <= SystemDictionaryHandles::Long_klass().raw_value(), + "must be one of the SystemDictionaryHandles"); + Register temp = rdi; + Label L_ok, L_bad; + BLOCK_COMMENT("verify_klass {"); + __ verify_oop(obj); + __ testptr(obj, obj); + __ jcc(Assembler::zero, L_bad); + __ push(temp); + __ load_klass(temp, obj); + __ cmpptr(temp, ExternalAddress((address) klass_addr)); + __ jcc(Assembler::equal, L_ok); + intptr_t super_check_offset = klass->super_check_offset(); + __ movptr(temp, Address(temp, super_check_offset)); + __ cmpptr(temp, ExternalAddress((address) klass_addr)); + __ jcc(Assembler::equal, L_ok); + __ pop(temp); + __ bind(L_bad); + __ stop(error_message); + __ BIND(L_ok); + __ pop(temp); + BLOCK_COMMENT("} verify_klass"); +} +#endif //ASSERT // Code generation address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) { @@ -116,6 +568,9 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* address entry_point = __ pc(); // fetch the MethodType from the method handle into rax (the 'check' register) + // FIXME: Interpreter should transmit pre-popped stack pointer, to locate base of arg list. + // This would simplify several touchy bits of code. + // See 6984712: JSR 292 method handle calls need a clean argument base pointer { Register tem = rbx_method; for (jint* pchase = methodOopDesc::method_type_offsets_chain(); (*pchase) != -1; pchase++) { @@ -128,17 +583,23 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* __ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp))); Register rdx_vmslots = rdx_temp; __ movl(rdx_vmslots, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::vmslots_offset_in_bytes, rdi_temp))); - __ movptr(rcx_recv, __ argument_address(rdx_vmslots)); + Address mh_receiver_slot_addr = __ argument_address(rdx_vmslots); + __ movptr(rcx_recv, mh_receiver_slot_addr); trace_method_handle(_masm, "invokeExact"); __ check_method_handle_type(rax_mtype, rcx_recv, rdi_temp, wrong_method_type); + + // Nobody uses the MH receiver slot after this. Make sure. + DEBUG_ONLY(__ movptr(mh_receiver_slot_addr, (int32_t)0x999999)); + __ jump_to_method_handle_entry(rcx_recv, rdi_temp); // for invokeGeneric (only), apply argument and result conversions on the fly __ bind(invoke_generic_slow_path); #ifdef ASSERT - { Label L; + if (VerifyMethodHandles) { + Label L; __ cmpb(Address(rbx_method, methodOopDesc::intrinsic_id_offset_in_bytes()), (int) vmIntrinsics::_invokeGeneric); __ jcc(Assembler::equal, L); __ stop("bad methodOop::intrinsic_id"); @@ -150,7 +611,7 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* // make room on the stack for another pointer: Register rcx_argslot = rcx_recv; __ lea(rcx_argslot, __ argument_address(rdx_vmslots, 1)); - insert_arg_slots(_masm, 2 * stack_move_unit(), _INSERT_REF_MASK, + insert_arg_slots(_masm, 2 * stack_move_unit(), rcx_argslot, rbx_temp, rdx_temp); // load up an adapter from the calling type (Java weaves this) @@ -185,40 +646,28 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* return entry_point; } +// Workaround for C++ overloading nastiness on '0' for RegisterOrConstant. +static RegisterOrConstant constant(int value) { + return RegisterOrConstant(value); +} + // Helper to insert argument slots into the stack. -// arg_slots must be a multiple of stack_move_unit() and <= 0 +// arg_slots must be a multiple of stack_move_unit() and < 0 +// rax_argslot is decremented to point to the new (shifted) location of the argslot +// But, rdx_temp ends up holding the original value of rax_argslot. void MethodHandles::insert_arg_slots(MacroAssembler* _masm, RegisterOrConstant arg_slots, - int arg_mask, Register rax_argslot, - Register rbx_temp, Register rdx_temp, Register temp3_reg) { - assert(temp3_reg == noreg, "temp3 not required"); + Register rbx_temp, Register rdx_temp) { + // allow constant zero + if (arg_slots.is_constant() && arg_slots.as_constant() == 0) + return; assert_different_registers(rax_argslot, rbx_temp, rdx_temp, (!arg_slots.is_register() ? rsp : arg_slots.as_register())); - -#ifdef ASSERT - verify_argslot(_masm, rax_argslot, "insertion point must fall within current frame"); - if (arg_slots.is_register()) { - Label L_ok, L_bad; - __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD); - __ jccb(Assembler::greater, L_bad); - __ testl(arg_slots.as_register(), -stack_move_unit() - 1); - __ jccb(Assembler::zero, L_ok); - __ bind(L_bad); - __ stop("assert arg_slots <= 0 and clear low bits"); - __ bind(L_ok); - } else { - assert(arg_slots.as_constant() <= 0, ""); - assert(arg_slots.as_constant() % -stack_move_unit() == 0, ""); - } -#endif //ASSERT - -#ifdef _LP64 - if (arg_slots.is_register()) { - // clean high bits of stack motion register (was loaded as an int) - __ movslq(arg_slots.as_register(), arg_slots.as_register()); - } -#endif + if (VerifyMethodHandles) + verify_argslot(_masm, rax_argslot, "insertion point must fall within current frame"); + if (VerifyMethodHandles) + verify_stack_move(_masm, arg_slots, -1); // Make space on the stack for the inserted argument(s). // Then pull down everything shallower than rax_argslot. @@ -230,59 +679,39 @@ void MethodHandles::insert_arg_slots(MacroAssembler* _masm, // argslot -= size; BLOCK_COMMENT("insert_arg_slots {"); __ mov(rdx_temp, rsp); // source pointer for copy - __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr)); + __ lea(rsp, Address(rsp, arg_slots, Interpreter::stackElementScale())); { Label loop; __ BIND(loop); // pull one word down each time through the loop __ movptr(rbx_temp, Address(rdx_temp, 0)); - __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp); + __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp); __ addptr(rdx_temp, wordSize); __ cmpptr(rdx_temp, rax_argslot); - __ jccb(Assembler::less, loop); + __ jcc(Assembler::less, loop); } // Now move the argslot down, to point to the opened-up space. - __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr)); + __ lea(rax_argslot, Address(rax_argslot, arg_slots, Interpreter::stackElementScale())); BLOCK_COMMENT("} insert_arg_slots"); } // Helper to remove argument slots from the stack. -// arg_slots must be a multiple of stack_move_unit() and >= 0 +// arg_slots must be a multiple of stack_move_unit() and > 0 void MethodHandles::remove_arg_slots(MacroAssembler* _masm, - RegisterOrConstant arg_slots, - Register rax_argslot, - Register rbx_temp, Register rdx_temp, Register temp3_reg) { - assert(temp3_reg == noreg, "temp3 not required"); + RegisterOrConstant arg_slots, + Register rax_argslot, + Register rbx_temp, Register rdx_temp) { + // allow constant zero + if (arg_slots.is_constant() && arg_slots.as_constant() == 0) + return; assert_different_registers(rax_argslot, rbx_temp, rdx_temp, (!arg_slots.is_register() ? rsp : arg_slots.as_register())); - -#ifdef ASSERT - // Verify that [argslot..argslot+size) lies within (rsp, rbp). - __ lea(rbx_temp, Address(rax_argslot, arg_slots, Address::times_ptr)); - verify_argslot(_masm, rbx_temp, "deleted argument(s) must fall within current frame"); - if (arg_slots.is_register()) { - Label L_ok, L_bad; - __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD); - __ jccb(Assembler::less, L_bad); - __ testl(arg_slots.as_register(), -stack_move_unit() - 1); - __ jccb(Assembler::zero, L_ok); - __ bind(L_bad); - __ stop("assert arg_slots >= 0 and clear low bits"); - __ bind(L_ok); - } else { - assert(arg_slots.as_constant() >= 0, ""); - assert(arg_slots.as_constant() % -stack_move_unit() == 0, ""); - } -#endif //ASSERT - -#ifdef _LP64 - if (false) { // not needed, since register is positive - // clean high bits of stack motion register (was loaded as an int) - if (arg_slots.is_register()) - __ movslq(arg_slots.as_register(), arg_slots.as_register()); - } -#endif + if (VerifyMethodHandles) + verify_argslots(_masm, arg_slots, rax_argslot, false, + "deleted argument(s) must fall within current frame"); + if (VerifyMethodHandles) + verify_stack_move(_masm, arg_slots, +1); BLOCK_COMMENT("remove_arg_slots {"); // Pull up everything shallower than rax_argslot. @@ -299,54 +728,332 @@ void MethodHandles::remove_arg_slots(MacroAssembler* _masm, __ BIND(loop); // pull one word up each time through the loop __ movptr(rbx_temp, Address(rdx_temp, 0)); - __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp); + __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp); __ addptr(rdx_temp, -wordSize); __ cmpptr(rdx_temp, rsp); - __ jccb(Assembler::greaterEqual, loop); + __ jcc(Assembler::greaterEqual, loop); } // Now move the argslot up, to point to the just-copied block. - __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr)); + __ lea(rsp, Address(rsp, arg_slots, Interpreter::stackElementScale())); // And adjust the argslot address to point at the deletion point. - __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr)); + __ lea(rax_argslot, Address(rax_argslot, arg_slots, Interpreter::stackElementScale())); BLOCK_COMMENT("} remove_arg_slots"); } +// Helper to copy argument slots to the top of the stack. +// The sequence starts with rax_argslot and is counted by slot_count +// slot_count must be a multiple of stack_move_unit() and >= 0 +// This function blows the temps but does not change rax_argslot. +void MethodHandles::push_arg_slots(MacroAssembler* _masm, + Register rax_argslot, + RegisterOrConstant slot_count, + int skip_words_count, + Register rbx_temp, Register rdx_temp) { + assert_different_registers(rax_argslot, rbx_temp, rdx_temp, + (!slot_count.is_register() ? rbp : slot_count.as_register()), + rsp); + assert(Interpreter::stackElementSize == wordSize, "else change this code"); + + if (VerifyMethodHandles) + verify_stack_move(_masm, slot_count, 0); + + // allow constant zero + if (slot_count.is_constant() && slot_count.as_constant() == 0) + return; + + BLOCK_COMMENT("push_arg_slots {"); + + Register rbx_top = rbx_temp; + + // There is at most 1 word to carry down with the TOS. + switch (skip_words_count) { + case 1: __ pop(rdx_temp); break; + case 0: break; + default: ShouldNotReachHere(); + } + + if (slot_count.is_constant()) { + for (int i = slot_count.as_constant() - 1; i >= 0; i--) { + __ pushptr(Address(rax_argslot, i * wordSize)); + } + } else { + Label L_plural, L_loop, L_break; + // Emit code to dynamically check for the common cases, zero and one slot. + __ cmpl(slot_count.as_register(), (int32_t) 1); + __ jccb(Assembler::greater, L_plural); + __ jccb(Assembler::less, L_break); + __ pushptr(Address(rax_argslot, 0)); + __ jmpb(L_break); + __ BIND(L_plural); + + // Loop for 2 or more: + // rbx = &rax[slot_count] + // while (rbx > rax) *(--rsp) = *(--rbx) + __ lea(rbx_top, Address(rax_argslot, slot_count, Address::times_ptr)); + __ BIND(L_loop); + __ subptr(rbx_top, wordSize); + __ pushptr(Address(rbx_top, 0)); + __ cmpptr(rbx_top, rax_argslot); + __ jcc(Assembler::above, L_loop); + __ bind(L_break); + } + switch (skip_words_count) { + case 1: __ push(rdx_temp); break; + case 0: break; + default: ShouldNotReachHere(); + } + BLOCK_COMMENT("} push_arg_slots"); +} + +// in-place movement; no change to rsp +// blows rax_temp, rdx_temp +void MethodHandles::move_arg_slots_up(MacroAssembler* _masm, + Register rbx_bottom, // invariant + Address top_addr, // can use rax_temp + RegisterOrConstant positive_distance_in_slots, + Register rax_temp, Register rdx_temp) { + BLOCK_COMMENT("move_arg_slots_up {"); + assert_different_registers(rbx_bottom, + rax_temp, rdx_temp, + positive_distance_in_slots.register_or_noreg()); + Label L_loop, L_break; + Register rax_top = rax_temp; + if (!top_addr.is_same_address(Address(rax_top, 0))) + __ lea(rax_top, top_addr); + // Detect empty (or broken) loop: +#ifdef ASSERT + if (VerifyMethodHandles) { + // Verify that &bottom < &top (non-empty interval) + Label L_ok, L_bad; + if (positive_distance_in_slots.is_register()) { + __ cmpptr(positive_distance_in_slots.as_register(), (int32_t) 0); + __ jcc(Assembler::lessEqual, L_bad); + } + __ cmpptr(rbx_bottom, rax_top); + __ jcc(Assembler::below, L_ok); + __ bind(L_bad); + __ stop("valid bounds (copy up)"); + __ BIND(L_ok); + } +#endif + __ cmpptr(rbx_bottom, rax_top); + __ jccb(Assembler::aboveEqual, L_break); + // work rax down to rbx, copying contiguous data upwards + // In pseudo-code: + // [rbx, rax) = &[bottom, top) + // while (--rax >= rbx) *(rax + distance) = *(rax + 0), rax--; + __ BIND(L_loop); + __ subptr(rax_top, wordSize); + __ movptr(rdx_temp, Address(rax_top, 0)); + __ movptr( Address(rax_top, positive_distance_in_slots, Address::times_ptr), rdx_temp); + __ cmpptr(rax_top, rbx_bottom); + __ jcc(Assembler::above, L_loop); + assert(Interpreter::stackElementSize == wordSize, "else change loop"); + __ bind(L_break); + BLOCK_COMMENT("} move_arg_slots_up"); +} + +// in-place movement; no change to rsp +// blows rax_temp, rdx_temp +void MethodHandles::move_arg_slots_down(MacroAssembler* _masm, + Address bottom_addr, // can use rax_temp + Register rbx_top, // invariant + RegisterOrConstant negative_distance_in_slots, + Register rax_temp, Register rdx_temp) { + BLOCK_COMMENT("move_arg_slots_down {"); + assert_different_registers(rbx_top, + negative_distance_in_slots.register_or_noreg(), + rax_temp, rdx_temp); + Label L_loop, L_break; + Register rax_bottom = rax_temp; + if (!bottom_addr.is_same_address(Address(rax_bottom, 0))) + __ lea(rax_bottom, bottom_addr); + // Detect empty (or broken) loop: +#ifdef ASSERT + assert(!negative_distance_in_slots.is_constant() || negative_distance_in_slots.as_constant() < 0, ""); + if (VerifyMethodHandles) { + // Verify that &bottom < &top (non-empty interval) + Label L_ok, L_bad; + if (negative_distance_in_slots.is_register()) { + __ cmpptr(negative_distance_in_slots.as_register(), (int32_t) 0); + __ jcc(Assembler::greaterEqual, L_bad); + } + __ cmpptr(rax_bottom, rbx_top); + __ jcc(Assembler::below, L_ok); + __ bind(L_bad); + __ stop("valid bounds (copy down)"); + __ BIND(L_ok); + } +#endif + __ cmpptr(rax_bottom, rbx_top); + __ jccb(Assembler::aboveEqual, L_break); + // work rax up to rbx, copying contiguous data downwards + // In pseudo-code: + // [rax, rbx) = &[bottom, top) + // while (rax < rbx) *(rax - distance) = *(rax + 0), rax++; + __ BIND(L_loop); + __ movptr(rdx_temp, Address(rax_bottom, 0)); + __ movptr( Address(rax_bottom, negative_distance_in_slots, Address::times_ptr), rdx_temp); + __ addptr(rax_bottom, wordSize); + __ cmpptr(rax_bottom, rbx_top); + __ jcc(Assembler::below, L_loop); + assert(Interpreter::stackElementSize == wordSize, "else change loop"); + __ bind(L_break); + BLOCK_COMMENT("} move_arg_slots_down"); +} + +// Copy from a field or array element to a stacked argument slot. +// is_element (ignored) says whether caller is loading an array element instead of an instance field. +void MethodHandles::move_typed_arg(MacroAssembler* _masm, + BasicType type, bool is_element, + Address slot_dest, Address value_src, + Register rbx_temp, Register rdx_temp) { + BLOCK_COMMENT(!is_element ? "move_typed_arg {" : "move_typed_arg { (array element)"); + if (type == T_OBJECT || type == T_ARRAY) { + __ load_heap_oop(rbx_temp, value_src); + __ movptr(slot_dest, rbx_temp); + } else if (type != T_VOID) { + int arg_size = type2aelembytes(type); + bool arg_is_signed = is_signed_subword_type(type); + int slot_size = (arg_size > wordSize) ? arg_size : wordSize; + __ load_sized_value( rdx_temp, value_src, arg_size, arg_is_signed, rbx_temp); + __ store_sized_value( slot_dest, rdx_temp, slot_size, rbx_temp); + } + BLOCK_COMMENT("} move_typed_arg"); +} + +void MethodHandles::move_return_value(MacroAssembler* _masm, BasicType type, + Address return_slot) { + BLOCK_COMMENT("move_return_value {"); + // Old versions of the JVM must clean the FPU stack after every return. +#ifndef _LP64 +#ifdef COMPILER2 + // The FPU stack is clean if UseSSE >= 2 but must be cleaned in other cases + if ((type == T_FLOAT && UseSSE < 1) || (type == T_DOUBLE && UseSSE < 2)) { + for (int i = 1; i < 8; i++) { + __ ffree(i); + } + } else if (UseSSE < 2) { + __ empty_FPU_stack(); + } +#endif //COMPILER2 +#endif //!_LP64 + + // Look at the type and pull the value out of the corresponding register. + if (type == T_VOID) { + // nothing to do + } else if (type == T_OBJECT) { + __ movptr(return_slot, rax); + } else if (type == T_INT || is_subword_type(type)) { + // write the whole word, even if only 32 bits is significant + __ movptr(return_slot, rax); + } else if (type == T_LONG) { + // store the value by parts + // Note: We assume longs are continguous (if misaligned) on the interpreter stack. + __ store_sized_value(return_slot, rax, BytesPerLong, rdx); + } else if (NOT_LP64((type == T_FLOAT && UseSSE < 1) || + (type == T_DOUBLE && UseSSE < 2) ||) + false) { + // Use old x86 FPU registers: + if (type == T_FLOAT) + __ fstp_s(return_slot); + else + __ fstp_d(return_slot); + } else if (type == T_FLOAT) { + __ movflt(return_slot, xmm0); + } else if (type == T_DOUBLE) { + __ movdbl(return_slot, xmm0); + } else { + ShouldNotReachHere(); + } + BLOCK_COMMENT("} move_return_value"); +} + + #ifndef PRODUCT extern "C" void print_method_handle(oop mh); void trace_method_handle_stub(const char* adaptername, - intptr_t* saved_sp, oop mh, - intptr_t* sp) { + intptr_t* saved_regs, + intptr_t* entry_sp, + intptr_t* saved_sp, + intptr_t* saved_bp) { // called as a leaf from native code: do not block the JVM! - intptr_t* entry_sp = sp + LP64_ONLY(16) NOT_LP64(8); - tty->print_cr("MH %s mh="INTPTR_FORMAT" sp="INTPTR_FORMAT" saved_sp="INTPTR_FORMAT")", - adaptername, (intptr_t)mh, (intptr_t)entry_sp, saved_sp); + intptr_t* last_sp = (intptr_t*) saved_bp[frame::interpreter_frame_last_sp_offset]; + intptr_t* base_sp = (intptr_t*) saved_bp[frame::interpreter_frame_monitor_block_top_offset]; + tty->print_cr("MH %s mh="INTPTR_FORMAT" sp=("INTPTR_FORMAT"+"INTX_FORMAT") stack_size="INTX_FORMAT" bp="INTPTR_FORMAT, + adaptername, (intptr_t)mh, (intptr_t)entry_sp, (intptr_t)(saved_sp - entry_sp), (intptr_t)(base_sp - last_sp), (intptr_t)saved_bp); + if (last_sp != saved_sp && last_sp != NULL) + tty->print_cr("*** last_sp="INTPTR_FORMAT, (intptr_t)last_sp); if (Verbose) { + tty->print(" reg dump: "); + int saved_regs_count = (entry_sp-1) - saved_regs; + // 32 bit: rdi rsi rbp rsp; rbx rdx rcx (*) rax + int i; + for (i = 0; i <= saved_regs_count; i++) { + if (i > 0 && i % 4 == 0 && i != saved_regs_count) { + tty->cr(); + tty->print(" + dump: "); + } + tty->print(" %d: "INTPTR_FORMAT, i, saved_regs[i]); + } + tty->cr(); + int stack_dump_count = 16; + if (stack_dump_count < (int)(saved_bp + 2 - saved_sp)) + stack_dump_count = (int)(saved_bp + 2 - saved_sp); + if (stack_dump_count > 64) stack_dump_count = 48; + for (i = 0; i < stack_dump_count; i += 4) { + tty->print_cr(" dump at SP[%d] "INTPTR_FORMAT": "INTPTR_FORMAT" "INTPTR_FORMAT" "INTPTR_FORMAT" "INTPTR_FORMAT, + i, (intptr_t) &entry_sp[i+0], entry_sp[i+0], entry_sp[i+1], entry_sp[i+2], entry_sp[i+3]); + } print_method_handle(mh); } } + +// The stub wraps the arguments in a struct on the stack to avoid +// dealing with the different calling conventions for passing 6 +// arguments. +struct MethodHandleStubArguments { + const char* adaptername; + oopDesc* mh; + intptr_t* saved_regs; + intptr_t* entry_sp; + intptr_t* saved_sp; + intptr_t* saved_bp; +}; +void trace_method_handle_stub_wrapper(MethodHandleStubArguments* args) { + trace_method_handle_stub(args->adaptername, + args->mh, + args->saved_regs, + args->entry_sp, + args->saved_sp, + args->saved_bp); +} + void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) { if (!TraceMethodHandles) return; BLOCK_COMMENT("trace_method_handle {"); + __ push(rax); + __ lea(rax, Address(rsp, wordSize * NOT_LP64(6) LP64_ONLY(14))); // entry_sp __ pusha(); __ pusha(); -#ifdef _LP64 - // Pass arguments carefully since the registers overlap with the calling convention. + __ mov(rbx, rsp); + __ enter(); + // incoming state: // rcx: method handle - // r13: saved sp - __ mov(c_rarg2, rcx); // mh - __ mov(c_rarg1, r13); // saved sp - __ mov(c_rarg3, rsp); // sp - __ movptr(c_rarg0, (intptr_t) adaptername); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), c_rarg0, c_rarg1, c_rarg2, c_rarg3); -#else - // arguments: - // rcx: method handle - // rsi: saved sp - __ movptr(rbx, (intptr_t) adaptername); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), rbx, rsi, rcx, rsp); -#endif + // r13 or rsi: saved sp + // To avoid calling convention issues, build a record on the stack and pass the pointer to that instead. + __ push(rbp); // saved_bp + __ push(rsi); // saved_sp + __ push(rax); // entry_sp + __ push(rbx); // pusha saved_regs + __ push(rcx); // mh + __ push(rcx); // adaptername + __ movptr(Address(rsp, 0), (intptr_t) adaptername); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub_wrapper), rsp); + __ leave(); __ popa(); + __ pop(rax); BLOCK_COMMENT("} trace_method_handle"); } #endif //PRODUCT @@ -358,13 +1065,21 @@ int MethodHandles::adapter_conversion_ops_supported_mask() { |(1<= _bound_ref_direct_mh); - BasicType arg_type = T_ILLEGAL; - int arg_mask = _INSERT_NO_MASK; - int arg_slots = -1; - get_ek_bound_mh_info(ek, arg_type, arg_mask, arg_slots); + BasicType arg_type = ek_bound_mh_arg_type(ek); + int arg_slots = type2size[arg_type]; // make room for the new argument: __ movl(rax_argslot, rcx_bmh_vmargslot); __ lea(rax_argslot, __ argument_address(rax_argslot)); - insert_arg_slots(_masm, arg_slots * stack_move_unit(), arg_mask, rax_argslot, rbx_temp, rdx_temp); + insert_arg_slots(_masm, arg_slots * stack_move_unit(), rax_argslot, rbx_temp, rdx_temp); // store bound argument into the new stack slot: __ load_heap_oop(rbx_temp, rcx_bmh_argument); @@ -589,9 +1311,10 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan __ movptr(Address(rax_argslot, 0), rbx_temp); } else { Address prim_value_addr(rbx_temp, java_lang_boxing_object::value_offset_in_bytes(arg_type)); - const int arg_size = type2aelembytes(arg_type); - __ load_sized_value(rdx_temp, prim_value_addr, arg_size, is_signed_subword_type(arg_type), rbx_temp); - __ store_sized_value(Address(rax_argslot, 0), rdx_temp, arg_size, rbx_temp); + move_typed_arg(_masm, arg_type, false, + Address(rax_argslot, 0), + prim_value_addr, + rbx_temp, rdx_temp); } if (direct_to_method) { @@ -628,7 +1351,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan // What class are we casting to? __ load_heap_oop(rbx_klass, rcx_amh_argument); // this is a Class object! - __ load_heap_oop(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes())); + load_klass_from_Class(_masm, rbx_klass); Label done; __ movptr(rdx_temp, vmarg); @@ -663,6 +1386,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan case _adapter_prim_to_prim: case _adapter_ref_to_prim: + case _adapter_prim_to_ref: // handled completely by optimized cases __ stop("init_AdapterMethodHandle should not issue this"); break; @@ -714,8 +1438,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan // Do the requested conversion and store the value. Register rbx_vminfo = rbx_temp; - __ movl(rbx_vminfo, rcx_amh_conversion); - assert(CONV_VMINFO_SHIFT == 0, "preshifted"); + load_conversion_vminfo(_masm, rbx_vminfo, rcx_amh_conversion); // get the new MH: __ load_heap_oop(rcx_recv, rcx_mh_vmtarget); @@ -753,7 +1476,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan // on a little-endian machine we keep the first slot and add another after __ lea(rax_argslot, __ argument_address(rax_argslot, 1)); - insert_arg_slots(_masm, stack_move_unit(), _INSERT_INT_MASK, + insert_arg_slots(_masm, stack_move_unit(), rax_argslot, rbx_temp, rdx_temp); Address vmarg1(rax_argslot, -Interpreter::stackElementSize); Address vmarg2 = vmarg1.plus_disp(Interpreter::stackElementSize); @@ -805,7 +1528,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan __ movl(rax_argslot, rcx_amh_vmargslot); __ lea(rax_argslot, __ argument_address(rax_argslot, 1)); if (ek == _adapter_opt_f2d) { - insert_arg_slots(_masm, stack_move_unit(), _INSERT_INT_MASK, + insert_arg_slots(_masm, stack_move_unit(), rax_argslot, rbx_temp, rdx_temp); } Address vmarg(rax_argslot, -Interpreter::stackElementSize); @@ -840,10 +1563,6 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan } break; - case _adapter_prim_to_ref: - __ unimplemented(entry_name(ek)); // %%% FIXME: NYI - break; - case _adapter_swap_args: case _adapter_rot_args: // handled completely by optimized cases @@ -857,8 +1576,8 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan case _adapter_opt_rot_2_up: case _adapter_opt_rot_2_down: { - int swap_bytes = 0, rotate = 0; - get_ek_adapter_opt_swap_rot_info(ek, swap_bytes, rotate); + int swap_slots = ek_adapter_opt_swap_slots(ek); + int rotate = ek_adapter_opt_swap_mode(ek); // 'argslot' is the position of the first argument to swap __ movl(rax_argslot, rcx_amh_vmargslot); @@ -866,83 +1585,69 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan // 'vminfo' is the second Register rbx_destslot = rbx_temp; - __ movl(rbx_destslot, rcx_amh_conversion); - assert(CONV_VMINFO_SHIFT == 0, "preshifted"); - __ andl(rbx_destslot, CONV_VMINFO_MASK); + load_conversion_vminfo(_masm, rbx_destslot, rcx_amh_conversion); __ lea(rbx_destslot, __ argument_address(rbx_destslot)); - DEBUG_ONLY(verify_argslot(_masm, rbx_destslot, "swap point must fall within current frame")); + if (VerifyMethodHandles) + verify_argslot(_masm, rbx_destslot, "swap point must fall within current frame"); + assert(Interpreter::stackElementSize == wordSize, "else rethink use of wordSize here"); if (!rotate) { - for (int i = 0; i < swap_bytes; i += wordSize) { - __ movptr(rdx_temp, Address(rax_argslot , i)); - __ push(rdx_temp); - __ movptr(rdx_temp, Address(rbx_destslot, i)); - __ movptr(Address(rax_argslot, i), rdx_temp); - __ pop(rdx_temp); - __ movptr(Address(rbx_destslot, i), rdx_temp); + // simple swap + for (int i = 0; i < swap_slots; i++) { + __ movptr(rdi_temp, Address(rax_argslot, i * wordSize)); + __ movptr(rdx_temp, Address(rbx_destslot, i * wordSize)); + __ movptr(Address(rax_argslot, i * wordSize), rdx_temp); + __ movptr(Address(rbx_destslot, i * wordSize), rdi_temp); } } else { - // push the first chunk, which is going to get overwritten - for (int i = swap_bytes; (i -= wordSize) >= 0; ) { - __ movptr(rdx_temp, Address(rax_argslot, i)); - __ push(rdx_temp); + // A rotate is actually pair of moves, with an "odd slot" (or pair) + // changing place with a series of other slots. + // First, push the "odd slot", which is going to get overwritten + for (int i = swap_slots - 1; i >= 0; i--) { + // handle one with rdi_temp instead of a push: + if (i == 0) __ movptr(rdi_temp, Address(rax_argslot, i * wordSize)); + else __ pushptr( Address(rax_argslot, i * wordSize)); } - if (rotate > 0) { - // rotate upward - __ subptr(rax_argslot, swap_bytes); -#ifdef ASSERT - { - // Verify that argslot > destslot, by at least swap_bytes. - Label L_ok; - __ cmpptr(rax_argslot, rbx_destslot); - __ jccb(Assembler::aboveEqual, L_ok); - __ stop("source must be above destination (upward rotation)"); - __ bind(L_ok); - } -#endif + // Here is rotate > 0: + // (low mem) (high mem) + // | dest: more_slots... | arg: odd_slot :arg+1 | + // => + // | dest: odd_slot | dest+1: more_slots... :arg+1 | // work argslot down to destslot, copying contiguous data upwards // pseudo-code: // rax = src_addr - swap_bytes // rbx = dest_addr // while (rax >= rbx) *(rax + swap_bytes) = *(rax + 0), rax--; - Label loop; - __ bind(loop); - __ movptr(rdx_temp, Address(rax_argslot, 0)); - __ movptr(Address(rax_argslot, swap_bytes), rdx_temp); - __ addptr(rax_argslot, -wordSize); - __ cmpptr(rax_argslot, rbx_destslot); - __ jccb(Assembler::aboveEqual, loop); + move_arg_slots_up(_masm, + rbx_destslot, + Address(rax_argslot, 0), + swap_slots, + rax_argslot, rdx_temp); } else { - __ addptr(rax_argslot, swap_bytes); -#ifdef ASSERT - { - // Verify that argslot < destslot, by at least swap_bytes. - Label L_ok; - __ cmpptr(rax_argslot, rbx_destslot); - __ jccb(Assembler::belowEqual, L_ok); - __ stop("source must be below destination (downward rotation)"); - __ bind(L_ok); - } -#endif + // Here is the other direction, rotate < 0: + // (low mem) (high mem) + // | arg: odd_slot | arg+1: more_slots... :dest+1 | + // => + // | arg: more_slots... | dest: odd_slot :dest+1 | // work argslot up to destslot, copying contiguous data downwards // pseudo-code: // rax = src_addr + swap_bytes // rbx = dest_addr // while (rax <= rbx) *(rax - swap_bytes) = *(rax + 0), rax++; - Label loop; - __ bind(loop); - __ movptr(rdx_temp, Address(rax_argslot, 0)); - __ movptr(Address(rax_argslot, -swap_bytes), rdx_temp); - __ addptr(rax_argslot, wordSize); - __ cmpptr(rax_argslot, rbx_destslot); - __ jccb(Assembler::belowEqual, loop); - } + __ addptr(rbx_destslot, wordSize); + move_arg_slots_down(_masm, + Address(rax_argslot, swap_slots * wordSize), + rbx_destslot, + -swap_slots, + rax_argslot, rdx_temp); + __ subptr(rbx_destslot, wordSize); + } // pop the original first chunk into the destination slot, now free - for (int i = 0; i < swap_bytes; i += wordSize) { - __ pop(rdx_temp); - __ movptr(Address(rbx_destslot, i), rdx_temp); + for (int i = 0; i < swap_slots; i++) { + if (i == 0) __ movptr(Address(rbx_destslot, i * wordSize), rdi_temp); + else __ popptr(Address(rbx_destslot, i * wordSize)); } } @@ -958,53 +1663,22 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan __ lea(rax_argslot, __ argument_address(rax_argslot)); // 'stack_move' is negative number of words to duplicate - Register rdx_stack_move = rdx_temp; - __ movl2ptr(rdx_stack_move, rcx_amh_conversion); - __ sarptr(rdx_stack_move, CONV_STACK_MOVE_SHIFT); + Register rdi_stack_move = rdi_temp; + load_stack_move(_masm, rdi_stack_move, rcx_recv, true); - int argslot0_num = 0; - Address argslot0 = __ argument_address(RegisterOrConstant(argslot0_num)); - assert(argslot0.base() == rsp, ""); - int pre_arg_size = argslot0.disp(); - assert(pre_arg_size % wordSize == 0, ""); - assert(pre_arg_size > 0, "must include PC"); - - // remember the old rsp+1 (argslot[0]) - Register rbx_oldarg = rbx_temp; - __ lea(rbx_oldarg, argslot0); - - // move rsp down to make room for dups - __ lea(rsp, Address(rsp, rdx_stack_move, Address::times_ptr)); - - // compute the new rsp+1 (argslot[0]) - Register rdx_newarg = rdx_temp; - __ lea(rdx_newarg, argslot0); - - __ push(rdi); // need a temp - // (preceding push must be done after arg addresses are taken!) - - // pull down the pre_arg_size data (PC) - for (int i = -pre_arg_size; i < 0; i += wordSize) { - __ movptr(rdi, Address(rbx_oldarg, i)); - __ movptr(Address(rdx_newarg, i), rdi); + if (VerifyMethodHandles) { + verify_argslots(_masm, rdi_stack_move, rax_argslot, true, + "copied argument(s) must fall within current frame"); } - // copy from rax_argslot[0...] down to new_rsp[1...] - // pseudo-code: - // rbx = old_rsp+1 - // rdx = new_rsp+1 - // rax = argslot - // while (rdx < rbx) *rdx++ = *rax++ - Label loop; - __ bind(loop); - __ movptr(rdi, Address(rax_argslot, 0)); - __ movptr(Address(rdx_newarg, 0), rdi); - __ addptr(rax_argslot, wordSize); - __ addptr(rdx_newarg, wordSize); - __ cmpptr(rdx_newarg, rbx_oldarg); - __ jccb(Assembler::less, loop); + // insert location is always the bottom of the argument list: + Address insert_location = __ argument_address(constant(0)); + int pre_arg_words = insert_location.disp() / wordSize; // return PC is pushed + assert(insert_location.base() == rsp, ""); - __ pop(rdi); // restore temp + __ negl(rdi_stack_move); + push_arg_slots(_masm, rax_argslot, rdi_stack_move, + pre_arg_words, rbx_temp, rdx_temp); __ load_heap_oop(rcx_recv, rcx_mh_vmtarget); __ jump_to_method_handle_entry(rcx_recv, rdx_temp); @@ -1017,63 +1691,583 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan __ movl(rax_argslot, rcx_amh_vmargslot); __ lea(rax_argslot, __ argument_address(rax_argslot)); - __ push(rdi); // need a temp // (must do previous push after argslot address is taken) // 'stack_move' is number of words to drop - Register rdi_stack_move = rdi; - __ movl2ptr(rdi_stack_move, rcx_amh_conversion); - __ sarptr(rdi_stack_move, CONV_STACK_MOVE_SHIFT); + Register rdi_stack_move = rdi_temp; + load_stack_move(_masm, rdi_stack_move, rcx_recv, false); remove_arg_slots(_masm, rdi_stack_move, rax_argslot, rbx_temp, rdx_temp); - __ pop(rdi); // restore temp - __ load_heap_oop(rcx_recv, rcx_mh_vmtarget); __ jump_to_method_handle_entry(rcx_recv, rdx_temp); } break; case _adapter_collect_args: - __ unimplemented(entry_name(ek)); // %%% FIXME: NYI - break; - + case _adapter_fold_args: case _adapter_spread_args: // handled completely by optimized cases __ stop("init_AdapterMethodHandle should not issue this"); break; + case _adapter_opt_collect_ref: + case _adapter_opt_collect_int: + case _adapter_opt_collect_long: + case _adapter_opt_collect_float: + case _adapter_opt_collect_double: + case _adapter_opt_collect_void: + case _adapter_opt_collect_0_ref: + case _adapter_opt_collect_1_ref: + case _adapter_opt_collect_2_ref: + case _adapter_opt_collect_3_ref: + case _adapter_opt_collect_4_ref: + case _adapter_opt_collect_5_ref: + case _adapter_opt_filter_S0_ref: + case _adapter_opt_filter_S1_ref: + case _adapter_opt_filter_S2_ref: + case _adapter_opt_filter_S3_ref: + case _adapter_opt_filter_S4_ref: + case _adapter_opt_filter_S5_ref: + case _adapter_opt_collect_2_S0_ref: + case _adapter_opt_collect_2_S1_ref: + case _adapter_opt_collect_2_S2_ref: + case _adapter_opt_collect_2_S3_ref: + case _adapter_opt_collect_2_S4_ref: + case _adapter_opt_collect_2_S5_ref: + case _adapter_opt_fold_ref: + case _adapter_opt_fold_int: + case _adapter_opt_fold_long: + case _adapter_opt_fold_float: + case _adapter_opt_fold_double: + case _adapter_opt_fold_void: + case _adapter_opt_fold_1_ref: + case _adapter_opt_fold_2_ref: + case _adapter_opt_fold_3_ref: + case _adapter_opt_fold_4_ref: + case _adapter_opt_fold_5_ref: + { + // Given a fresh incoming stack frame, build a new ricochet frame. + // On entry, TOS points at a return PC, and RBP is the callers frame ptr. + // RSI/R13 has the caller's exact stack pointer, which we must also preserve. + // RCX contains an AdapterMethodHandle of the indicated kind. + + // Relevant AMH fields: + // amh.vmargslot: + // points to the trailing edge of the arguments + // to filter, collect, or fold. For a boxing operation, + // it points just after the single primitive value. + // amh.argument: + // recursively called MH, on |collect| arguments + // amh.vmtarget: + // final destination MH, on return value, etc. + // amh.conversion.dest: + // tells what is the type of the return value + // (not needed here, since dest is also derived from ek) + // amh.conversion.vminfo: + // points to the trailing edge of the return value + // when the vmtarget is to be called; this is + // equal to vmargslot + (retained ? |collect| : 0) + + // Pass 0 or more argument slots to the recursive target. + int collect_count_constant = ek_adapter_opt_collect_count(ek); + + // The collected arguments are copied from the saved argument list: + int collect_slot_constant = ek_adapter_opt_collect_slot(ek); + + assert(ek_orig == _adapter_collect_args || + ek_orig == _adapter_fold_args, ""); + bool retain_original_args = (ek_orig == _adapter_fold_args); + + // The return value is replaced (or inserted) at the 'vminfo' argslot. + // Sometimes we can compute this statically. + int dest_slot_constant = -1; + if (!retain_original_args) + dest_slot_constant = collect_slot_constant; + else if (collect_slot_constant >= 0 && collect_count_constant >= 0) + // We are preserving all the arguments, and the return value is prepended, + // so the return slot is to the left (above) the |collect| sequence. + dest_slot_constant = collect_slot_constant + collect_count_constant; + + // Replace all those slots by the result of the recursive call. + // The result type can be one of ref, int, long, float, double, void. + // In the case of void, nothing is pushed on the stack after return. + BasicType dest = ek_adapter_opt_collect_type(ek); + assert(dest == type2wfield[dest], "dest is a stack slot type"); + int dest_count = type2size[dest]; + assert(dest_count == 1 || dest_count == 2 || (dest_count == 0 && dest == T_VOID), "dest has a size"); + + // Choose a return continuation. + EntryKind ek_ret = _adapter_opt_return_any; + if (dest != T_CONFLICT && OptimizeMethodHandles) { + switch (dest) { + case T_INT : ek_ret = _adapter_opt_return_int; break; + case T_LONG : ek_ret = _adapter_opt_return_long; break; + case T_FLOAT : ek_ret = _adapter_opt_return_float; break; + case T_DOUBLE : ek_ret = _adapter_opt_return_double; break; + case T_OBJECT : ek_ret = _adapter_opt_return_ref; break; + case T_VOID : ek_ret = _adapter_opt_return_void; break; + default : ShouldNotReachHere(); + } + if (dest == T_OBJECT && dest_slot_constant >= 0) { + EntryKind ek_try = EntryKind(_adapter_opt_return_S0_ref + dest_slot_constant); + if (ek_try <= _adapter_opt_return_LAST && + ek_adapter_opt_return_slot(ek_try) == dest_slot_constant) { + ek_ret = ek_try; + } + } + assert(ek_adapter_opt_return_type(ek_ret) == dest, ""); + } + + // Already pushed: ... keep1 | collect | keep2 | sender_pc | + // push(sender_pc); + + // Compute argument base: + Register rax_argv = rax_argslot; + __ lea(rax_argv, __ argument_address(constant(0))); + + // Push a few extra argument words, if we need them to store the return value. + { + int extra_slots = 0; + if (retain_original_args) { + extra_slots = dest_count; + } else if (collect_count_constant == -1) { + extra_slots = dest_count; // collect_count might be zero; be generous + } else if (dest_count > collect_count_constant) { + extra_slots = (dest_count - collect_count_constant); + } else { + // else we know we have enough dead space in |collect| to repurpose for return values + } + DEBUG_ONLY(extra_slots += 1); + if (extra_slots > 0) { + __ pop(rbx_temp); // return value + __ subptr(rsp, (extra_slots * Interpreter::stackElementSize)); + // Push guard word #2 in debug mode. + DEBUG_ONLY(__ movptr(Address(rsp, 0), (int32_t) RicochetFrame::MAGIC_NUMBER_2)); + __ push(rbx_temp); + } + } + + RicochetFrame::enter_ricochet_frame(_masm, rcx_recv, rax_argv, + entry(ek_ret)->from_interpreted_entry(), rbx_temp); + + // Now pushed: ... keep1 | collect | keep2 | RF | + // some handy frame slots: + Address exact_sender_sp_addr = RicochetFrame::frame_address(RicochetFrame::exact_sender_sp_offset_in_bytes()); + Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes()); + Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes()); + +#ifdef ASSERT + if (VerifyMethodHandles && dest != T_CONFLICT) { + BLOCK_COMMENT("verify AMH.conv.dest"); + load_conversion_dest_type(_masm, rbx_temp, conversion_addr); + Label L_dest_ok; + __ cmpl(rbx_temp, (int) dest); + __ jcc(Assembler::equal, L_dest_ok); + if (dest == T_INT) { + for (int bt = T_BOOLEAN; bt < T_INT; bt++) { + if (is_subword_type(BasicType(bt))) { + __ cmpl(rbx_temp, (int) bt); + __ jcc(Assembler::equal, L_dest_ok); + } + } + } + __ stop("bad dest in AMH.conv"); + __ BIND(L_dest_ok); + } +#endif //ASSERT + + // Find out where the original copy of the recursive argument sequence begins. + Register rax_coll = rax_argv; + { + RegisterOrConstant collect_slot = collect_slot_constant; + if (collect_slot_constant == -1) { + __ movl(rdi_temp, rcx_amh_vmargslot); + collect_slot = rdi_temp; + } + if (collect_slot_constant != 0) + __ lea(rax_coll, Address(rax_argv, collect_slot, Interpreter::stackElementScale())); + // rax_coll now points at the trailing edge of |collect| and leading edge of |keep2| + } + + // Replace the old AMH with the recursive MH. (No going back now.) + // In the case of a boxing call, the recursive call is to a 'boxer' method, + // such as Integer.valueOf or Long.valueOf. In the case of a filter + // or collect call, it will take one or more arguments, transform them, + // and return some result, to store back into argument_base[vminfo]. + __ load_heap_oop(rcx_recv, rcx_amh_argument); + if (VerifyMethodHandles) verify_method_handle(_masm, rcx_recv); + + // Push a space for the recursively called MH first: + __ push((int32_t)NULL_WORD); + + // Calculate |collect|, the number of arguments we are collecting. + Register rdi_collect_count = rdi_temp; + RegisterOrConstant collect_count; + if (collect_count_constant >= 0) { + collect_count = collect_count_constant; + } else { + __ load_method_handle_vmslots(rdi_collect_count, rcx_recv, rdx_temp); + collect_count = rdi_collect_count; + } +#ifdef ASSERT + if (VerifyMethodHandles && collect_count_constant >= 0) { + __ load_method_handle_vmslots(rbx_temp, rcx_recv, rdx_temp); + Label L_count_ok; + __ cmpl(rbx_temp, collect_count_constant); + __ jcc(Assembler::equal, L_count_ok); + __ stop("bad vminfo in AMH.conv"); + __ BIND(L_count_ok); + } +#endif //ASSERT + + // copy |collect| slots directly to TOS: + push_arg_slots(_masm, rax_coll, collect_count, 0, rbx_temp, rdx_temp); + // Now pushed: ... keep1 | collect | keep2 | RF... | collect | + // rax_coll still points at the trailing edge of |collect| and leading edge of |keep2| + + // If necessary, adjust the saved arguments to make room for the eventual return value. + // Normal adjustment: ... keep1 | +dest+ | -collect- | keep2 | RF... | collect | + // If retaining args: ... keep1 | +dest+ | collect | keep2 | RF... | collect | + // In the non-retaining case, this might move keep2 either up or down. + // We don't have to copy the whole | RF... collect | complex, + // but we must adjust RF.saved_args_base. + // Also, from now on, we will forget about the origial copy of |collect|. + // If we are retaining it, we will treat it as part of |keep2|. + // For clarity we will define |keep3| = |collect|keep2| or |keep2|. + + BLOCK_COMMENT("adjust trailing arguments {"); + // Compare the sizes of |+dest+| and |-collect-|, which are opposed opening and closing movements. + int open_count = dest_count; + RegisterOrConstant close_count = collect_count_constant; + Register rdi_close_count = rdi_collect_count; + if (retain_original_args) { + close_count = constant(0); + } else if (collect_count_constant == -1) { + close_count = rdi_collect_count; + } + + // How many slots need moving? This is simply dest_slot (0 => no |keep3|). + RegisterOrConstant keep3_count; + Register rsi_keep3_count = rsi; // can repair from RF.exact_sender_sp + if (dest_slot_constant >= 0) { + keep3_count = dest_slot_constant; + } else { + load_conversion_vminfo(_masm, rsi_keep3_count, conversion_addr); + keep3_count = rsi_keep3_count; + } +#ifdef ASSERT + if (VerifyMethodHandles && dest_slot_constant >= 0) { + load_conversion_vminfo(_masm, rbx_temp, conversion_addr); + Label L_vminfo_ok; + __ cmpl(rbx_temp, dest_slot_constant); + __ jcc(Assembler::equal, L_vminfo_ok); + __ stop("bad vminfo in AMH.conv"); + __ BIND(L_vminfo_ok); + } +#endif //ASSERT + + // tasks remaining: + bool move_keep3 = (!keep3_count.is_constant() || keep3_count.as_constant() != 0); + bool stomp_dest = (NOT_DEBUG(dest == T_OBJECT) DEBUG_ONLY(dest_count != 0)); + bool fix_arg_base = (!close_count.is_constant() || open_count != close_count.as_constant()); + + if (stomp_dest | fix_arg_base) { + // we will probably need an updated rax_argv value + if (collect_slot_constant >= 0) { + // rax_coll already holds the leading edge of |keep2|, so tweak it + assert(rax_coll == rax_argv, "elided a move"); + if (collect_slot_constant != 0) + __ subptr(rax_argv, collect_slot_constant * Interpreter::stackElementSize); + } else { + // Just reload from RF.saved_args_base. + __ movptr(rax_argv, saved_args_base_addr); + } + } + + // Old and new argument locations (based at slot 0). + // Net shift (&new_argv - &old_argv) is (close_count - open_count). + bool zero_open_count = (open_count == 0); // remember this bit of info + if (move_keep3 && fix_arg_base) { + // It will be easier t have everything in one register: + if (close_count.is_register()) { + // Deduct open_count from close_count register to get a clean +/- value. + __ subptr(close_count.as_register(), open_count); + } else { + close_count = close_count.as_constant() - open_count; + } + open_count = 0; + } + Address old_argv(rax_argv, 0); + Address new_argv(rax_argv, close_count, Interpreter::stackElementScale(), + - open_count * Interpreter::stackElementSize); + + // First decide if any actual data are to be moved. + // We can skip if (a) |keep3| is empty, or (b) the argument list size didn't change. + // (As it happens, all movements involve an argument list size change.) + + // If there are variable parameters, use dynamic checks to skip around the whole mess. + Label L_done; + if (!keep3_count.is_constant()) { + __ testl(keep3_count.as_register(), keep3_count.as_register()); + __ jcc(Assembler::zero, L_done); + } + if (!close_count.is_constant()) { + __ cmpl(close_count.as_register(), open_count); + __ jcc(Assembler::equal, L_done); + } + + if (move_keep3 && fix_arg_base) { + bool emit_move_down = false, emit_move_up = false, emit_guard = false; + if (!close_count.is_constant()) { + emit_move_down = emit_guard = !zero_open_count; + emit_move_up = true; + } else if (open_count != close_count.as_constant()) { + emit_move_down = (open_count > close_count.as_constant()); + emit_move_up = !emit_move_down; + } + Label L_move_up; + if (emit_guard) { + __ cmpl(close_count.as_register(), open_count); + __ jcc(Assembler::greater, L_move_up); + } + + if (emit_move_down) { + // Move arguments down if |+dest+| > |-collect-| + // (This is rare, except when arguments are retained.) + // This opens space for the return value. + if (keep3_count.is_constant()) { + for (int i = 0; i < keep3_count.as_constant(); i++) { + __ movptr(rdx_temp, old_argv.plus_disp(i * Interpreter::stackElementSize)); + __ movptr( new_argv.plus_disp(i * Interpreter::stackElementSize), rdx_temp); + } + } else { + Register rbx_argv_top = rbx_temp; + __ lea(rbx_argv_top, old_argv.plus_disp(keep3_count, Interpreter::stackElementScale())); + move_arg_slots_down(_masm, + old_argv, // beginning of old argv + rbx_argv_top, // end of old argv + close_count, // distance to move down (must be negative) + rax_argv, rdx_temp); + // Used argv as an iteration variable; reload from RF.saved_args_base. + __ movptr(rax_argv, saved_args_base_addr); + } + } + + if (emit_guard) { + __ jmp(L_done); // assumes emit_move_up is true also + __ BIND(L_move_up); + } + + if (emit_move_up) { + + // Move arguments up if |+dest+| < |-collect-| + // (This is usual, except when |keep3| is empty.) + // This closes up the space occupied by the now-deleted collect values. + if (keep3_count.is_constant()) { + for (int i = keep3_count.as_constant() - 1; i >= 0; i--) { + __ movptr(rdx_temp, old_argv.plus_disp(i * Interpreter::stackElementSize)); + __ movptr( new_argv.plus_disp(i * Interpreter::stackElementSize), rdx_temp); + } + } else { + Address argv_top = old_argv.plus_disp(keep3_count, Interpreter::stackElementScale()); + move_arg_slots_up(_masm, + rax_argv, // beginning of old argv + argv_top, // end of old argv + close_count, // distance to move up (must be positive) + rbx_temp, rdx_temp); + } + } + } + __ BIND(L_done); + + if (fix_arg_base) { + // adjust RF.saved_args_base by adding (close_count - open_count) + if (!new_argv.is_same_address(Address(rax_argv, 0))) + __ lea(rax_argv, new_argv); + __ movptr(saved_args_base_addr, rax_argv); + } + + if (stomp_dest) { + // Stomp the return slot, so it doesn't hold garbage. + // This isn't strictly necessary, but it may help detect bugs. + int forty_two = RicochetFrame::RETURN_VALUE_PLACEHOLDER; + __ movptr(Address(rax_argv, keep3_count, Address::times_ptr), + (int32_t) forty_two); + // uses rsi_keep3_count + } + BLOCK_COMMENT("} adjust trailing arguments"); + + BLOCK_COMMENT("do_recursive_call"); + __ mov(saved_last_sp, rsp); // set rsi/r13 for callee + __ pushptr(ExternalAddress(SharedRuntime::ricochet_blob()->bounce_addr()).addr()); + // The globally unique bounce address has two purposes: + // 1. It helps the JVM recognize this frame (frame::is_ricochet_frame). + // 2. When returned to, it cuts back the stack and redirects control flow + // to the return handler. + // The return handler will further cut back the stack when it takes + // down the RF. Perhaps there is a way to streamline this further. + + // State during recursive call: + // ... keep1 | dest | dest=42 | keep3 | RF... | collect | bounce_pc | + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + + break; + } + + case _adapter_opt_return_ref: + case _adapter_opt_return_int: + case _adapter_opt_return_long: + case _adapter_opt_return_float: + case _adapter_opt_return_double: + case _adapter_opt_return_void: + case _adapter_opt_return_S0_ref: + case _adapter_opt_return_S1_ref: + case _adapter_opt_return_S2_ref: + case _adapter_opt_return_S3_ref: + case _adapter_opt_return_S4_ref: + case _adapter_opt_return_S5_ref: + { + BasicType dest_type_constant = ek_adapter_opt_return_type(ek); + int dest_slot_constant = ek_adapter_opt_return_slot(ek); + + if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm); + + if (dest_slot_constant == -1) { + // The current stub is a general handler for this dest_type. + // It can be called from _adapter_opt_return_any below. + // Stash the address in a little table. + assert((dest_type_constant & CONV_TYPE_MASK) == dest_type_constant, "oob"); + address return_handler = __ pc(); + _adapter_return_handlers[dest_type_constant] = return_handler; + if (dest_type_constant == T_INT) { + // do the subword types too + for (int bt = T_BOOLEAN; bt < T_INT; bt++) { + if (is_subword_type(BasicType(bt)) && + _adapter_return_handlers[bt] == NULL) { + _adapter_return_handlers[bt] = return_handler; + } + } + } + } + + Register rbx_arg_base = rbx_temp; + assert_different_registers(rax, rdx, // possibly live return value registers + rdi_temp, rbx_arg_base); + + Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes()); + Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes()); + + __ movptr(rbx_arg_base, saved_args_base_addr); + RegisterOrConstant dest_slot = dest_slot_constant; + if (dest_slot_constant == -1) { + load_conversion_vminfo(_masm, rdi_temp, conversion_addr); + dest_slot = rdi_temp; + } + // Store the result back into the argslot. + // This code uses the interpreter calling sequence, in which the return value + // is usually left in the TOS register, as defined by InterpreterMacroAssembler::pop. + // There are certain irregularities with floating point values, which can be seen + // in TemplateInterpreterGenerator::generate_return_entry_for. + move_return_value(_masm, dest_type_constant, Address(rbx_arg_base, dest_slot, Interpreter::stackElementScale())); + + RicochetFrame::leave_ricochet_frame(_masm, rcx_recv, rbx_arg_base, rdx_temp); + __ push(rdx_temp); // repush the return PC + + // Load the final target and go. + if (VerifyMethodHandles) verify_method_handle(_masm, rcx_recv); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + __ hlt(); // -------------------- + break; + } + + case _adapter_opt_return_any: + { + if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm); + Register rdi_conv = rdi_temp; + assert_different_registers(rax, rdx, // possibly live return value registers + rdi_conv, rbx_temp); + + Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes()); + load_conversion_dest_type(_masm, rdi_conv, conversion_addr); + __ lea(rbx_temp, ExternalAddress((address) &_adapter_return_handlers[0])); + __ movptr(rbx_temp, Address(rbx_temp, rdi_conv, Address::times_ptr)); + +#ifdef ASSERT + { Label L_badconv; + __ testptr(rbx_temp, rbx_temp); + __ jccb(Assembler::zero, L_badconv); + __ jmp(rbx_temp); + __ bind(L_badconv); + __ stop("bad method handle return"); + } +#else //ASSERT + __ jmp(rbx_temp); +#endif //ASSERT + break; + } + case _adapter_opt_spread_0: - case _adapter_opt_spread_1: - case _adapter_opt_spread_more: + case _adapter_opt_spread_1_ref: + case _adapter_opt_spread_2_ref: + case _adapter_opt_spread_3_ref: + case _adapter_opt_spread_4_ref: + case _adapter_opt_spread_5_ref: + case _adapter_opt_spread_ref: + case _adapter_opt_spread_byte: + case _adapter_opt_spread_char: + case _adapter_opt_spread_short: + case _adapter_opt_spread_int: + case _adapter_opt_spread_long: + case _adapter_opt_spread_float: + case _adapter_opt_spread_double: { // spread an array out into a group of arguments - int length_constant = get_ek_adapter_opt_spread_info(ek); + int length_constant = ek_adapter_opt_spread_count(ek); + bool length_can_be_zero = (length_constant == 0); + if (length_constant < 0) { + // some adapters with variable length must handle the zero case + if (!OptimizeMethodHandles || + ek_adapter_opt_spread_type(ek) != T_OBJECT) + length_can_be_zero = true; + } // find the address of the array argument __ movl(rax_argslot, rcx_amh_vmargslot); __ lea(rax_argslot, __ argument_address(rax_argslot)); - // grab some temps - { __ push(rsi); __ push(rdi); } - // (preceding pushes must be done after argslot address is taken!) -#define UNPUSH_RSI_RDI \ - { __ pop(rdi); __ pop(rsi); } + // grab another temp + Register rsi_temp = rsi; + { if (rsi_temp == saved_last_sp) __ push(saved_last_sp); } + // (preceding push must be done after argslot address is taken!) +#define UNPUSH_RSI \ + { if (rsi_temp == saved_last_sp) __ pop(saved_last_sp); } // arx_argslot points both to the array and to the first output arg vmarg = Address(rax_argslot, 0); // Get the array value. - Register rsi_array = rsi; + Register rsi_array = rsi_temp; Register rdx_array_klass = rdx_temp; - BasicType elem_type = T_OBJECT; + BasicType elem_type = ek_adapter_opt_spread_type(ek); + int elem_slots = type2size[elem_type]; // 1 or 2 + int array_slots = 1; // array is always a T_OBJECT int length_offset = arrayOopDesc::length_offset_in_bytes(); int elem0_offset = arrayOopDesc::base_offset_in_bytes(elem_type); __ movptr(rsi_array, vmarg); - Label skip_array_check; - if (length_constant == 0) { + + Label L_array_is_empty, L_insert_arg_space, L_copy_args, L_args_done; + if (length_can_be_zero) { + // handle the null pointer case, if zero is allowed + Label L_skip; + if (length_constant < 0) { + load_conversion_vminfo(_masm, rbx_temp, rcx_amh_conversion); + __ testl(rbx_temp, rbx_temp); + __ jcc(Assembler::notZero, L_skip); + } __ testptr(rsi_array, rsi_array); - __ jcc(Assembler::zero, skip_array_check); + __ jcc(Assembler::zero, L_array_is_empty); + __ bind(L_skip); } __ null_check(rsi_array, oopDesc::klass_offset_in_bytes()); __ load_klass(rdx_array_klass, rsi_array); @@ -1081,22 +2275,20 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan // Check the array type. Register rbx_klass = rbx_temp; __ load_heap_oop(rbx_klass, rcx_amh_argument); // this is a Class object! - __ load_heap_oop(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes())); + load_klass_from_Class(_masm, rbx_klass); Label ok_array_klass, bad_array_klass, bad_array_length; - __ check_klass_subtype(rdx_array_klass, rbx_klass, rdi, ok_array_klass); + __ check_klass_subtype(rdx_array_klass, rbx_klass, rdi_temp, ok_array_klass); // If we get here, the type check failed! __ jmp(bad_array_klass); - __ bind(ok_array_klass); + __ BIND(ok_array_klass); // Check length. if (length_constant >= 0) { __ cmpl(Address(rsi_array, length_offset), length_constant); } else { Register rbx_vminfo = rbx_temp; - __ movl(rbx_vminfo, rcx_amh_conversion); - assert(CONV_VMINFO_SHIFT == 0, "preshifted"); - __ andl(rbx_vminfo, CONV_VMINFO_MASK); + load_conversion_vminfo(_masm, rbx_vminfo, rcx_amh_conversion); __ cmpl(rbx_vminfo, Address(rsi_array, length_offset)); } __ jcc(Assembler::notEqual, bad_array_length); @@ -1108,90 +2300,104 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan // Form a pointer to the end of the affected region. __ lea(rdx_argslot_limit, Address(rax_argslot, Interpreter::stackElementSize)); // 'stack_move' is negative number of words to insert - Register rdi_stack_move = rdi; - __ movl2ptr(rdi_stack_move, rcx_amh_conversion); - __ sarptr(rdi_stack_move, CONV_STACK_MOVE_SHIFT); + // This number already accounts for elem_slots. + Register rdi_stack_move = rdi_temp; + load_stack_move(_masm, rdi_stack_move, rcx_recv, true); + __ cmpptr(rdi_stack_move, 0); + assert(stack_move_unit() < 0, "else change this comparison"); + __ jcc(Assembler::less, L_insert_arg_space); + __ jcc(Assembler::equal, L_copy_args); + // single argument case, with no array movement + __ BIND(L_array_is_empty); + remove_arg_slots(_masm, -stack_move_unit() * array_slots, + rax_argslot, rbx_temp, rdx_temp); + __ jmp(L_args_done); // no spreading to do + __ BIND(L_insert_arg_space); + // come here in the usual case, stack_move < 0 (2 or more spread arguments) Register rsi_temp = rsi_array; // spill this - insert_arg_slots(_masm, rdi_stack_move, -1, + insert_arg_slots(_masm, rdi_stack_move, rax_argslot, rbx_temp, rsi_temp); - // reload the array (since rsi was killed) - __ movptr(rsi_array, vmarg); - } else if (length_constant > 1) { - int arg_mask = 0; - int new_slots = (length_constant - 1); - for (int i = 0; i < new_slots; i++) { - arg_mask <<= 1; - arg_mask |= _INSERT_REF_MASK; - } - insert_arg_slots(_masm, new_slots * stack_move_unit(), arg_mask, + // reload the array since rsi was killed + // reload from rdx_argslot_limit since rax_argslot is now decremented + __ movptr(rsi_array, Address(rdx_argslot_limit, -Interpreter::stackElementSize)); + } else if (length_constant >= 1) { + int new_slots = (length_constant * elem_slots) - array_slots; + insert_arg_slots(_masm, new_slots * stack_move_unit(), rax_argslot, rbx_temp, rdx_temp); - } else if (length_constant == 1) { - // no stack resizing required } else if (length_constant == 0) { - remove_arg_slots(_masm, -stack_move_unit(), + __ BIND(L_array_is_empty); + remove_arg_slots(_masm, -stack_move_unit() * array_slots, rax_argslot, rbx_temp, rdx_temp); + } else { + ShouldNotReachHere(); } // Copy from the array to the new slots. // Note: Stack change code preserves integrity of rax_argslot pointer. // So even after slot insertions, rax_argslot still points to first argument. + // Beware: Arguments that are shallow on the stack are deep in the array, + // and vice versa. So a downward-growing stack (the usual) has to be copied + // elementwise in reverse order from the source array. + __ BIND(L_copy_args); if (length_constant == -1) { // [rax_argslot, rdx_argslot_limit) is the area we are inserting into. + // Array element [0] goes at rdx_argslot_limit[-wordSize]. Register rsi_source = rsi_array; __ lea(rsi_source, Address(rsi_array, elem0_offset)); + Register rdx_fill_ptr = rdx_argslot_limit; Label loop; - __ bind(loop); - __ movptr(rbx_temp, Address(rsi_source, 0)); - __ movptr(Address(rax_argslot, 0), rbx_temp); + __ BIND(loop); + __ addptr(rdx_fill_ptr, -Interpreter::stackElementSize * elem_slots); + move_typed_arg(_masm, elem_type, true, + Address(rdx_fill_ptr, 0), Address(rsi_source, 0), + rbx_temp, rdi_temp); __ addptr(rsi_source, type2aelembytes(elem_type)); - __ addptr(rax_argslot, Interpreter::stackElementSize); - __ cmpptr(rax_argslot, rdx_argslot_limit); - __ jccb(Assembler::less, loop); + __ cmpptr(rdx_fill_ptr, rax_argslot); + __ jcc(Assembler::greater, loop); } else if (length_constant == 0) { - __ bind(skip_array_check); // nothing to copy } else { int elem_offset = elem0_offset; - int slot_offset = 0; + int slot_offset = length_constant * Interpreter::stackElementSize; for (int index = 0; index < length_constant; index++) { - __ movptr(rbx_temp, Address(rsi_array, elem_offset)); - __ movptr(Address(rax_argslot, slot_offset), rbx_temp); + slot_offset -= Interpreter::stackElementSize * elem_slots; // fill backward + move_typed_arg(_masm, elem_type, true, + Address(rax_argslot, slot_offset), Address(rsi_array, elem_offset), + rbx_temp, rdi_temp); elem_offset += type2aelembytes(elem_type); - slot_offset += Interpreter::stackElementSize; } } + __ BIND(L_args_done); // Arguments are spread. Move to next method handle. - UNPUSH_RSI_RDI; + UNPUSH_RSI; __ load_heap_oop(rcx_recv, rcx_mh_vmtarget); __ jump_to_method_handle_entry(rcx_recv, rdx_temp); __ bind(bad_array_klass); - UNPUSH_RSI_RDI; + UNPUSH_RSI; assert(!vmarg.uses(rarg2_required), "must be different registers"); - __ movptr(rarg2_required, Address(rdx_array_klass, java_mirror_offset)); // required type - __ movptr(rarg1_actual, vmarg); // bad array - __ movl( rarg0_code, (int) Bytecodes::_aaload); // who is complaining? + __ load_heap_oop( rarg2_required, Address(rdx_array_klass, java_mirror_offset)); // required type + __ movptr( rarg1_actual, vmarg); // bad array + __ movl( rarg0_code, (int) Bytecodes::_aaload); // who is complaining? __ jump(ExternalAddress(from_interpreted_entry(_raise_exception))); __ bind(bad_array_length); - UNPUSH_RSI_RDI; + UNPUSH_RSI; assert(!vmarg.uses(rarg2_required), "must be different registers"); - __ mov (rarg2_required, rcx_recv); // AMH requiring a certain length - __ movptr(rarg1_actual, vmarg); // bad array - __ movl( rarg0_code, (int) Bytecodes::_arraylength); // who is complaining? + __ mov( rarg2_required, rcx_recv); // AMH requiring a certain length + __ movptr( rarg1_actual, vmarg); // bad array + __ movl( rarg0_code, (int) Bytecodes::_arraylength); // who is complaining? __ jump(ExternalAddress(from_interpreted_entry(_raise_exception))); +#undef UNPUSH_RSI -#undef UNPUSH_RSI_RDI + break; } - break; - case _adapter_flyby: - case _adapter_ricochet: - __ unimplemented(entry_name(ek)); // %%% FIXME: NYI - break; - - default: ShouldNotReachHere(); + default: + // do not require all platforms to recognize all adapter types + __ nop(); + return; } __ hlt(); diff --git a/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp b/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp new file mode 100644 index 00000000000..2a93500d7e5 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2010, 2011, 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. + * + */ + +// Platform-specific definitions for method handles. +// These definitions are inlined into class MethodHandles. + +public: + +// The stack just after the recursive call from a ricochet frame +// looks something like this. Offsets are marked in words, not bytes. +// rsi (r13 on LP64) is part of the interpreter calling sequence +// which tells the callee where is my real rsp (for frame walking). +// (...lower memory addresses) +// rsp: [ return pc ] always the global RicochetBlob::bounce_addr +// rsp+1: [ recursive arg N ] +// rsp+2: [ recursive arg N-1 ] +// ... +// rsp+N: [ recursive arg 1 ] +// rsp+N+1: [ recursive method handle ] +// ... +// rbp-6: [ cleanup continuation pc ] <-- (struct RicochetFrame) +// rbp-5: [ saved target MH ] the MH we will call on the saved args +// rbp-4: [ saved args layout oop ] an int[] array which describes argument layout +// rbp-3: [ saved args pointer ] address of transformed adapter arg M (slot 0) +// rbp-2: [ conversion ] information about how the return value is used +// rbp-1: [ exact sender sp ] exact TOS (rsi/r13) of original sender frame +// rbp+0: [ saved sender fp ] (for original sender of AMH) +// rbp+1: [ saved sender pc ] (back to original sender of AMH) +// rbp+2: [ transformed adapter arg M ] <-- (extended TOS of original sender) +// rbp+3: [ transformed adapter arg M-1] +// ... +// rbp+M+1: [ transformed adapter arg 1 ] +// rbp+M+2: [ padding ] <-- (rbp + saved args base offset) +// ... [ optional padding] +// (higher memory addresses...) +// +// The arguments originally passed by the original sender +// are lost, and arbitrary amounts of stack motion might have +// happened due to argument transformation. +// (This is done by C2I/I2C adapters and non-direct method handles.) +// This is why there is an unpredictable amount of memory between +// the extended and exact TOS of the sender. +// The ricochet adapter itself will also (in general) perform +// transformations before the recursive call. +// +// The transformed and saved arguments, immediately above the saved +// return PC, are a well-formed method handle invocation ready to execute. +// When the GC needs to walk the stack, these arguments are described +// via the saved arg types oop, an int[] array with a private format. +// This array is derived from the type of the transformed adapter +// method handle, which also sits at the base of the saved argument +// bundle. Since the GC may not be able to fish out the int[] +// array, so it is pushed explicitly on the stack. This may be +// an unnecessary expense. +// +// The following register conventions are significant at this point: +// rsp the thread stack, as always; preserved by caller +// rsi/r13 exact TOS of recursive frame (contents of [rbp-2]) +// rcx recursive method handle (contents of [rsp+N+1]) +// rbp preserved by caller (not used by caller) +// Unless otherwise specified, all registers can be blown by the call. +// +// If this frame must be walked, the transformed adapter arguments +// will be found with the help of the saved arguments descriptor. +// +// Therefore, the descriptor must match the referenced arguments. +// The arguments must be followed by at least one word of padding, +// which will be necessary to complete the final method handle call. +// That word is not treated as holding an oop. Neither is the word +// +// The word pointed to by the return argument pointer is not +// treated as an oop, even if points to a saved argument. +// This allows the saved argument list to have a "hole" in it +// to receive an oop from the recursive call. +// (The hole might temporarily contain RETURN_VALUE_PLACEHOLDER.) +// +// When the recursive callee returns, RicochetBlob::bounce_addr will +// immediately jump to the continuation stored in the RF. +// This continuation will merge the recursive return value +// into the saved argument list. At that point, the original +// rsi, rbp, and rsp will be reloaded, the ricochet frame will +// disappear, and the final target of the adapter method handle +// will be invoked on the transformed argument list. + +class RicochetFrame { + friend class MethodHandles; + + private: + intptr_t* _continuation; // what to do when control gets back here + oopDesc* _saved_target; // target method handle to invoke on saved_args + oopDesc* _saved_args_layout; // caching point for MethodTypeForm.vmlayout cookie + intptr_t* _saved_args_base; // base of pushed arguments (slot 0, arg N) (-3) + intptr_t _conversion; // misc. information from original AdapterMethodHandle (-2) + intptr_t* _exact_sender_sp; // parallel to interpreter_frame_sender_sp (-1) + intptr_t* _sender_link; // *must* coincide with frame::link_offset (0) + address _sender_pc; // *must* coincide with frame::return_addr_offset (1) + + public: + intptr_t* continuation() const { return _continuation; } + oop saved_target() const { return _saved_target; } + oop saved_args_layout() const { return _saved_args_layout; } + intptr_t* saved_args_base() const { return _saved_args_base; } + intptr_t conversion() const { return _conversion; } + intptr_t* exact_sender_sp() const { return _exact_sender_sp; } + intptr_t* sender_link() const { return _sender_link; } + address sender_pc() const { return _sender_pc; } + + intptr_t* extended_sender_sp() const { return saved_args_base(); } + + intptr_t return_value_slot_number() const { + return adapter_conversion_vminfo(conversion()); + } + BasicType return_value_type() const { + return adapter_conversion_dest_type(conversion()); + } + bool has_return_value_slot() const { + return return_value_type() != T_VOID; + } + intptr_t* return_value_slot_addr() const { + assert(has_return_value_slot(), ""); + return saved_arg_slot_addr(return_value_slot_number()); + } + intptr_t* saved_target_slot_addr() const { + return saved_arg_slot_addr(saved_args_length()); + } + intptr_t* saved_arg_slot_addr(int slot) const { + assert(slot >= 0, ""); + return (intptr_t*)( (address)saved_args_base() + (slot * Interpreter::stackElementSize) ); + } + + jint saved_args_length() const; + jint saved_arg_offset(int arg) const; + + // GC interface + oop* saved_target_addr() { return (oop*)&_saved_target; } + oop* saved_args_layout_addr() { return (oop*)&_saved_args_layout; } + + oop compute_saved_args_layout(bool read_cache, bool write_cache); + + // Compiler/assembler interface. + static int continuation_offset_in_bytes() { return offset_of(RicochetFrame, _continuation); } + static int saved_target_offset_in_bytes() { return offset_of(RicochetFrame, _saved_target); } + static int saved_args_layout_offset_in_bytes(){ return offset_of(RicochetFrame, _saved_args_layout); } + static int saved_args_base_offset_in_bytes() { return offset_of(RicochetFrame, _saved_args_base); } + static int conversion_offset_in_bytes() { return offset_of(RicochetFrame, _conversion); } + static int exact_sender_sp_offset_in_bytes() { return offset_of(RicochetFrame, _exact_sender_sp); } + static int sender_link_offset_in_bytes() { return offset_of(RicochetFrame, _sender_link); } + static int sender_pc_offset_in_bytes() { return offset_of(RicochetFrame, _sender_pc); } + + // This value is not used for much, but it apparently must be nonzero. + static int frame_size_in_bytes() { return sender_link_offset_in_bytes(); } + +#ifdef ASSERT + // The magic number is supposed to help find ricochet frames within the bytes of stack dumps. + enum { MAGIC_NUMBER_1 = 0xFEED03E, MAGIC_NUMBER_2 = 0xBEEF03E }; + static int magic_number_1_offset_in_bytes() { return -wordSize; } + static int magic_number_2_offset_in_bytes() { return sizeof(RicochetFrame); } + intptr_t magic_number_1() const { return *(intptr_t*)((address)this + magic_number_1_offset_in_bytes()); }; + intptr_t magic_number_2() const { return *(intptr_t*)((address)this + magic_number_2_offset_in_bytes()); }; +#endif //ASSERT + + enum { RETURN_VALUE_PLACEHOLDER = (NOT_DEBUG(0) DEBUG_ONLY(42)) }; + + static void verify_offsets() NOT_DEBUG_RETURN; + void verify() const NOT_DEBUG_RETURN; // check for MAGIC_NUMBER, etc. + void zap_arguments() NOT_DEBUG_RETURN; + + static void generate_ricochet_blob(MacroAssembler* _masm, + // output params: + int* frame_size_in_words, int* bounce_offset, int* exception_offset); + + static void enter_ricochet_frame(MacroAssembler* _masm, + Register rcx_recv, + Register rax_argv, + address return_handler, + Register rbx_temp); + static void leave_ricochet_frame(MacroAssembler* _masm, + Register rcx_recv, + Register new_sp_reg, + Register sender_pc_reg); + + static Address frame_address(int offset = 0) { + // The RicochetFrame is found by subtracting a constant offset from rbp. + return Address(rbp, - sender_link_offset_in_bytes() + offset); + } + + static RicochetFrame* from_frame(const frame& fr) { + address bp = (address) fr.fp(); + RicochetFrame* rf = (RicochetFrame*)(bp - sender_link_offset_in_bytes()); + rf->verify(); + return rf; + } + + static void verify_clean(MacroAssembler* _masm) NOT_DEBUG_RETURN; +}; + +// Additional helper methods for MethodHandles code generation: +public: + static void load_klass_from_Class(MacroAssembler* _masm, Register klass_reg); + static void load_conversion_vminfo(MacroAssembler* _masm, Register reg, Address conversion_field_addr); + static void load_conversion_dest_type(MacroAssembler* _masm, Register reg, Address conversion_field_addr); + + static void load_stack_move(MacroAssembler* _masm, + Register rdi_stack_move, + Register rcx_amh, + bool might_be_negative); + + static void insert_arg_slots(MacroAssembler* _masm, + RegisterOrConstant arg_slots, + Register rax_argslot, + Register rbx_temp, Register rdx_temp); + + static void remove_arg_slots(MacroAssembler* _masm, + RegisterOrConstant arg_slots, + Register rax_argslot, + Register rbx_temp, Register rdx_temp); + + static void push_arg_slots(MacroAssembler* _masm, + Register rax_argslot, + RegisterOrConstant slot_count, + int skip_words_count, + Register rbx_temp, Register rdx_temp); + + static void move_arg_slots_up(MacroAssembler* _masm, + Register rbx_bottom, // invariant + Address top_addr, // can use rax_temp + RegisterOrConstant positive_distance_in_slots, + Register rax_temp, Register rdx_temp); + + static void move_arg_slots_down(MacroAssembler* _masm, + Address bottom_addr, // can use rax_temp + Register rbx_top, // invariant + RegisterOrConstant negative_distance_in_slots, + Register rax_temp, Register rdx_temp); + + static void move_typed_arg(MacroAssembler* _masm, + BasicType type, bool is_element, + Address slot_dest, Address value_src, + Register rbx_temp, Register rdx_temp); + + static void move_return_value(MacroAssembler* _masm, BasicType type, + Address return_slot); + + static void verify_argslot(MacroAssembler* _masm, Register argslot_reg, + const char* error_message) NOT_DEBUG_RETURN; + + static void verify_argslots(MacroAssembler* _masm, + RegisterOrConstant argslot_count, + Register argslot_reg, + bool negate_argslot, + const char* error_message) NOT_DEBUG_RETURN; + + static void verify_stack_move(MacroAssembler* _masm, + RegisterOrConstant arg_slots, + int direction) NOT_DEBUG_RETURN; + + static void verify_klass(MacroAssembler* _masm, + Register obj, KlassHandle klass, + const char* error_message = "wrong klass") NOT_DEBUG_RETURN; + + static void verify_method_handle(MacroAssembler* _masm, Register mh_reg) { + verify_klass(_masm, mh_reg, SystemDictionaryHandles::MethodHandle_klass(), + "reference is a MH"); + } + + static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN; + + static Register saved_last_sp_register() { + // Should be in sharedRuntime, not here. + return LP64_ONLY(r13) NOT_LP64(rsi); + } diff --git a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp index 149bc7c9ea4..bb32cf34dab 100644 --- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp @@ -2253,6 +2253,31 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } +//----------------------------generate_ricochet_blob--------------------------- +void SharedRuntime::generate_ricochet_blob() { + if (!EnableInvokeDynamic) return; // leave it as a null + + // allocate space for the code + ResourceMark rm; + // setup code generation tools + CodeBuffer buffer("ricochet_blob", 256, 256); + MacroAssembler* masm = new MacroAssembler(&buffer); + + int frame_size_in_words = -1, bounce_offset = -1, exception_offset = -1; + MethodHandles::RicochetFrame::generate_ricochet_blob(masm, &frame_size_in_words, &bounce_offset, &exception_offset); + + // ------------- + // make sure all code is generated + masm->flush(); + + // failed to generate? + if (frame_size_in_words < 0 || bounce_offset < 0 || exception_offset < 0) { + assert(false, "bad ricochet blob"); + return; + } + + _ricochet_blob = RicochetBlob::create(&buffer, bounce_offset, exception_offset, frame_size_in_words); +} //------------------------------generate_deopt_blob---------------------------- void SharedRuntime::generate_deopt_blob() { @@ -2996,6 +3021,8 @@ void SharedRuntime::generate_stubs() { generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), true); + generate_ricochet_blob(); + generate_deopt_blob(); #ifdef COMPILER2 generate_uncommon_trap_blob(); diff --git a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp index c017cf86d47..89766d1f3f7 100644 --- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp @@ -2530,6 +2530,32 @@ uint SharedRuntime::out_preserve_stack_slots() { } +//----------------------------generate_ricochet_blob--------------------------- +void SharedRuntime::generate_ricochet_blob() { + if (!EnableInvokeDynamic) return; // leave it as a null + + // allocate space for the code + ResourceMark rm; + // setup code generation tools + CodeBuffer buffer("ricochet_blob", 512, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + int frame_size_in_words = -1, bounce_offset = -1, exception_offset = -1; + MethodHandles::RicochetFrame::generate_ricochet_blob(masm, &frame_size_in_words, &bounce_offset, &exception_offset); + + // ------------- + // make sure all code is generated + masm->flush(); + + // failed to generate? + if (frame_size_in_words < 0 || bounce_offset < 0 || exception_offset < 0) { + assert(false, "bad ricochet blob"); + return; + } + + _ricochet_blob = RicochetBlob::create(&buffer, bounce_offset, exception_offset, frame_size_in_words); +} + //------------------------------generate_deopt_blob---------------------------- void SharedRuntime::generate_deopt_blob() { // Allocate space for the code @@ -3205,6 +3231,8 @@ void SharedRuntime::generate_stubs() { generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), true); + generate_ricochet_blob(); + generate_deopt_blob(); #ifdef COMPILER2 diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp index 283274e3dde..65b131b3757 100644 --- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp @@ -36,7 +36,7 @@ enum platform_dependent_constants { // MethodHandles adapters enum method_handles_platform_dependent_constants { - method_handles_adapters_code_size = 10000 + method_handles_adapters_code_size = 30000 DEBUG_ONLY(+ 10000) }; class x86 { diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp index b1726caa692..ac6f2fcd853 100644 --- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp @@ -38,7 +38,7 @@ enum platform_dependent_constants { // MethodHandles adapters enum method_handles_platform_dependent_constants { - method_handles_adapters_code_size = 40000 + method_handles_adapters_code_size = 80000 DEBUG_ONLY(+ 120000) }; class x86 { diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp index fa422673d48..eb512306f38 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.cpp +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -2602,6 +2602,7 @@ int java_lang_invoke_MethodType::ptype_count(oop mt) { // Support for java_lang_invoke_MethodTypeForm int java_lang_invoke_MethodTypeForm::_vmslots_offset; +int java_lang_invoke_MethodTypeForm::_vmlayout_offset; int java_lang_invoke_MethodTypeForm::_erasedType_offset; int java_lang_invoke_MethodTypeForm::_genericInvoker_offset; @@ -2609,6 +2610,7 @@ void java_lang_invoke_MethodTypeForm::compute_offsets() { klassOop k = SystemDictionary::MethodTypeForm_klass(); if (k != NULL) { compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true); + compute_optional_offset(_vmlayout_offset, k, vmSymbols::vmlayout_name(), vmSymbols::object_signature()); compute_optional_offset(_erasedType_offset, k, vmSymbols::erasedType_name(), vmSymbols::java_lang_invoke_MethodType_signature(), true); compute_optional_offset(_genericInvoker_offset, k, vmSymbols::genericInvoker_name(), vmSymbols::java_lang_invoke_MethodHandle_signature(), true); if (_genericInvoker_offset == 0) _genericInvoker_offset = -1; // set to explicit "empty" value @@ -2617,9 +2619,31 @@ void java_lang_invoke_MethodTypeForm::compute_offsets() { int java_lang_invoke_MethodTypeForm::vmslots(oop mtform) { assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only"); + assert(_vmslots_offset > 0, ""); return mtform->int_field(_vmslots_offset); } +oop java_lang_invoke_MethodTypeForm::vmlayout(oop mtform) { + assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only"); + assert(_vmlayout_offset > 0, ""); + return mtform->obj_field(_vmlayout_offset); +} + +oop java_lang_invoke_MethodTypeForm::init_vmlayout(oop mtform, oop cookie) { + assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only"); + oop previous = vmlayout(mtform); + if (previous != NULL) { + return previous; // someone else beat us to it + } + HeapWord* cookie_addr = (HeapWord*) mtform->obj_field_addr(_vmlayout_offset); + OrderAccess::storestore(); // make sure our copy is fully committed + previous = oopDesc::atomic_compare_exchange_oop(cookie, cookie_addr, previous); + if (previous != NULL) { + return previous; // someone else beat us to it + } + return cookie; +} + oop java_lang_invoke_MethodTypeForm::erasedType(oop mtform) { assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only"); return mtform->obj_field(_erasedType_offset); diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp index 5701bc3893c..eb104b84361 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -949,18 +949,19 @@ class java_lang_invoke_AdapterMethodHandle: public java_lang_invoke_BoundMethodH OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive - OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI) + OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg) OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg) OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS) OP_DROP_ARGS = 0x9, // remove one or more argument slots - OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI) + OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size) - OP_FLYBY = 0xC, // operate first on reified argument list (NYI) - OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI) + OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result + //OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists CONV_OP_LIMIT = 0xE, // limit of CONV_OP enumeration CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field + CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK @@ -1089,6 +1090,7 @@ class java_lang_invoke_MethodTypeForm: AllStatic { private: static int _vmslots_offset; // number of argument slots needed + static int _vmlayout_offset; // object describing internal calling sequence static int _erasedType_offset; // erasedType = canonical MethodType static int _genericInvoker_offset; // genericInvoker = adapter for invokeGeneric @@ -1100,8 +1102,12 @@ class java_lang_invoke_MethodTypeForm: AllStatic { static oop erasedType(oop mtform); static oop genericInvoker(oop mtform); + static oop vmlayout(oop mtform); + static oop init_vmlayout(oop mtform, oop cookie); + // Accessors for code generation: static int vmslots_offset_in_bytes() { return _vmslots_offset; } + static int vmlayout_offset_in_bytes() { return _vmlayout_offset; } static int erasedType_offset_in_bytes() { return _erasedType_offset; } static int genericInvoker_offset_in_bytes() { return _genericInvoker_offset; } }; diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp index dd185e6bc98..9d870ba1b9d 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -2362,8 +2362,15 @@ methodOop SystemDictionary::find_method_handle_invoke(Symbol* name, spe = invoke_method_table()->find_entry(index, hash, signature, name_id); if (spe == NULL) spe = invoke_method_table()->add_entry(index, hash, signature, name_id); - if (spe->property_oop() == NULL) + if (spe->property_oop() == NULL) { spe->set_property_oop(m()); + // Link m to his method type, if it is suitably generic. + oop mtform = java_lang_invoke_MethodType::form(mt()); + if (mtform != NULL && mt() == java_lang_invoke_MethodTypeForm::erasedType(mtform) + && java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() > 0) { + java_lang_invoke_MethodTypeForm::init_vmlayout(mtform, m()); + } + } } else { non_cached_result = m; } diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index 88d946854da..73c5cfa5c75 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -341,6 +341,7 @@ template(vmtarget_name, "vmtarget") \ template(vmentry_name, "vmentry") \ template(vmslots_name, "vmslots") \ + template(vmlayout_name, "vmlayout") \ template(vmindex_name, "vmindex") \ template(vmargslot_name, "vmargslot") \ template(flags_name, "flags") \ @@ -393,6 +394,7 @@ template(void_signature, "V") \ template(byte_array_signature, "[B") \ template(char_array_signature, "[C") \ + template(int_array_signature, "[I") \ template(object_void_signature, "(Ljava/lang/Object;)V") \ template(object_int_signature, "(Ljava/lang/Object;)I") \ template(object_boolean_signature, "(Ljava/lang/Object;)Z") \ diff --git a/hotspot/src/share/vm/code/codeBlob.cpp b/hotspot/src/share/vm/code/codeBlob.cpp index dead37cc336..244c32043f3 100644 --- a/hotspot/src/share/vm/code/codeBlob.cpp +++ b/hotspot/src/share/vm/code/codeBlob.cpp @@ -152,6 +152,32 @@ void CodeBlob::set_oop_maps(OopMapSet* p) { } +void CodeBlob::trace_new_stub(CodeBlob* stub, const char* name1, const char* name2) { + // Do not hold the CodeCache lock during name formatting. + assert(!CodeCache_lock->owned_by_self(), "release CodeCache before registering the stub"); + + if (stub != NULL) { + char stub_id[256]; + assert(strlen(name1) + strlen(name2) < sizeof(stub_id), ""); + jio_snprintf(stub_id, sizeof(stub_id), "%s%s", name1, name2); + if (PrintStubCode) { + tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, (intptr_t) stub); + Disassembler::decode(stub->code_begin(), stub->code_end()); + } + Forte::register_stub(stub_id, stub->code_begin(), stub->code_end()); + + if (JvmtiExport::should_post_dynamic_code_generated()) { + const char* stub_name = name2; + if (name2[0] == '\0') stub_name = name1; + JvmtiExport::post_dynamic_code_generated(stub_name, stub->code_begin(), stub->code_end()); + } + } + + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); +} + + void CodeBlob::flush() { if (_oop_maps) { FREE_C_HEAP_ARRAY(unsigned char, _oop_maps); @@ -312,23 +338,7 @@ RuntimeStub* RuntimeStub::new_runtime_stub(const char* stub_name, stub = new (size) RuntimeStub(stub_name, cb, size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments); } - // Do not hold the CodeCache lock during name formatting. - if (stub != NULL) { - char stub_id[256]; - jio_snprintf(stub_id, sizeof(stub_id), "RuntimeStub - %s", stub_name); - if (PrintStubCode) { - tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, stub); - Disassembler::decode(stub->code_begin(), stub->code_end()); - } - Forte::register_stub(stub_id, stub->code_begin(), stub->code_end()); - - if (JvmtiExport::should_post_dynamic_code_generated()) { - JvmtiExport::post_dynamic_code_generated(stub_name, stub->code_begin(), stub->code_end()); - } - } - - // Track memory usage statistic after releasing CodeCache_lock - MemoryService::track_code_cache_memory_usage(); + trace_new_stub(stub, "RuntimeStub - ", stub_name); return stub; } @@ -340,6 +350,50 @@ void* RuntimeStub::operator new(size_t s, unsigned size) { return p; } +// operator new shared by all singletons: +void* SingletonBlob::operator new(size_t s, unsigned size) { + void* p = CodeCache::allocate(size); + if (!p) fatal("Initial size of CodeCache is too small"); + return p; +} + + +//---------------------------------------------------------------------------------------------------- +// Implementation of RicochetBlob + +RicochetBlob::RicochetBlob( + CodeBuffer* cb, + int size, + int bounce_offset, + int exception_offset, + int frame_size +) +: SingletonBlob("RicochetBlob", cb, sizeof(RicochetBlob), size, frame_size, (OopMapSet*) NULL) +{ + _bounce_offset = bounce_offset; + _exception_offset = exception_offset; +} + + +RicochetBlob* RicochetBlob::create( + CodeBuffer* cb, + int bounce_offset, + int exception_offset, + int frame_size) +{ + RicochetBlob* blob = NULL; + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + unsigned int size = allocation_size(cb, sizeof(RicochetBlob)); + blob = new (size) RicochetBlob(cb, size, bounce_offset, exception_offset, frame_size); + } + + trace_new_stub(blob, "RicochetBlob"); + + return blob; +} + //---------------------------------------------------------------------------------------------------- // Implementation of DeoptimizationBlob @@ -386,34 +440,12 @@ DeoptimizationBlob* DeoptimizationBlob::create( frame_size); } - // Do not hold the CodeCache lock during name formatting. - if (blob != NULL) { - char blob_id[256]; - jio_snprintf(blob_id, sizeof(blob_id), "DeoptimizationBlob@" PTR_FORMAT, blob->code_begin()); - if (PrintStubCode) { - tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob); - Disassembler::decode(blob->code_begin(), blob->code_end()); - } - Forte::register_stub(blob_id, blob->code_begin(), blob->code_end()); - - if (JvmtiExport::should_post_dynamic_code_generated()) { - JvmtiExport::post_dynamic_code_generated("DeoptimizationBlob", blob->code_begin(), blob->code_end()); - } - } - - // Track memory usage statistic after releasing CodeCache_lock - MemoryService::track_code_cache_memory_usage(); + trace_new_stub(blob, "DeoptimizationBlob"); return blob; } -void* DeoptimizationBlob::operator new(size_t s, unsigned size) { - void* p = CodeCache::allocate(size); - if (!p) fatal("Initial size of CodeCache is too small"); - return p; -} - //---------------------------------------------------------------------------------------------------- // Implementation of UncommonTrapBlob @@ -441,33 +473,12 @@ UncommonTrapBlob* UncommonTrapBlob::create( blob = new (size) UncommonTrapBlob(cb, size, oop_maps, frame_size); } - // Do not hold the CodeCache lock during name formatting. - if (blob != NULL) { - char blob_id[256]; - jio_snprintf(blob_id, sizeof(blob_id), "UncommonTrapBlob@" PTR_FORMAT, blob->code_begin()); - if (PrintStubCode) { - tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob); - Disassembler::decode(blob->code_begin(), blob->code_end()); - } - Forte::register_stub(blob_id, blob->code_begin(), blob->code_end()); - - if (JvmtiExport::should_post_dynamic_code_generated()) { - JvmtiExport::post_dynamic_code_generated("UncommonTrapBlob", blob->code_begin(), blob->code_end()); - } - } - - // Track memory usage statistic after releasing CodeCache_lock - MemoryService::track_code_cache_memory_usage(); + trace_new_stub(blob, "UncommonTrapBlob"); return blob; } -void* UncommonTrapBlob::operator new(size_t s, unsigned size) { - void* p = CodeCache::allocate(size); - if (!p) fatal("Initial size of CodeCache is too small"); - return p; -} #endif // COMPILER2 @@ -498,33 +509,12 @@ ExceptionBlob* ExceptionBlob::create( blob = new (size) ExceptionBlob(cb, size, oop_maps, frame_size); } - // We do not need to hold the CodeCache lock during name formatting - if (blob != NULL) { - char blob_id[256]; - jio_snprintf(blob_id, sizeof(blob_id), "ExceptionBlob@" PTR_FORMAT, blob->code_begin()); - if (PrintStubCode) { - tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob); - Disassembler::decode(blob->code_begin(), blob->code_end()); - } - Forte::register_stub(blob_id, blob->code_begin(), blob->code_end()); - - if (JvmtiExport::should_post_dynamic_code_generated()) { - JvmtiExport::post_dynamic_code_generated("ExceptionBlob", blob->code_begin(), blob->code_end()); - } - } - - // Track memory usage statistic after releasing CodeCache_lock - MemoryService::track_code_cache_memory_usage(); + trace_new_stub(blob, "ExceptionBlob"); return blob; } -void* ExceptionBlob::operator new(size_t s, unsigned size) { - void* p = CodeCache::allocate(size); - if (!p) fatal("Initial size of CodeCache is too small"); - return p; -} #endif // COMPILER2 @@ -554,35 +544,12 @@ SafepointBlob* SafepointBlob::create( blob = new (size) SafepointBlob(cb, size, oop_maps, frame_size); } - // We do not need to hold the CodeCache lock during name formatting. - if (blob != NULL) { - char blob_id[256]; - jio_snprintf(blob_id, sizeof(blob_id), "SafepointBlob@" PTR_FORMAT, blob->code_begin()); - if (PrintStubCode) { - tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob); - Disassembler::decode(blob->code_begin(), blob->code_end()); - } - Forte::register_stub(blob_id, blob->code_begin(), blob->code_end()); - - if (JvmtiExport::should_post_dynamic_code_generated()) { - JvmtiExport::post_dynamic_code_generated("SafepointBlob", blob->code_begin(), blob->code_end()); - } - } - - // Track memory usage statistic after releasing CodeCache_lock - MemoryService::track_code_cache_memory_usage(); + trace_new_stub(blob, "SafepointBlob"); return blob; } -void* SafepointBlob::operator new(size_t s, unsigned size) { - void* p = CodeCache::allocate(size); - if (!p) fatal("Initial size of CodeCache is too small"); - return p; -} - - //---------------------------------------------------------------------------------------------------- // Verification and printing diff --git a/hotspot/src/share/vm/code/codeBlob.hpp b/hotspot/src/share/vm/code/codeBlob.hpp index 61e64882d43..d653794885f 100644 --- a/hotspot/src/share/vm/code/codeBlob.hpp +++ b/hotspot/src/share/vm/code/codeBlob.hpp @@ -35,6 +35,7 @@ // Suptypes are: // nmethod : Compiled Java methods (include method that calls to native code) // RuntimeStub : Call to VM runtime methods +// RicochetBlob : Used for blocking MethodHandle adapters // DeoptimizationBlob : Used for deoptimizatation // ExceptionBlob : Used for stack unrolling // SafepointBlob : Used to handle illegal instruction exceptions @@ -95,12 +96,13 @@ class CodeBlob VALUE_OBJ_CLASS_SPEC { void flush(); // Typing - virtual bool is_buffer_blob() const { return false; } - virtual bool is_nmethod() const { return false; } - virtual bool is_runtime_stub() const { return false; } - virtual bool is_deoptimization_stub() const { return false; } - virtual bool is_uncommon_trap_stub() const { return false; } - virtual bool is_exception_stub() const { return false; } + virtual bool is_buffer_blob() const { return false; } + virtual bool is_nmethod() const { return false; } + virtual bool is_runtime_stub() const { return false; } + virtual bool is_ricochet_stub() const { return false; } + virtual bool is_deoptimization_stub() const { return false; } + virtual bool is_uncommon_trap_stub() const { return false; } + virtual bool is_exception_stub() const { return false; } virtual bool is_safepoint_stub() const { return false; } virtual bool is_adapter_blob() const { return false; } virtual bool is_method_handles_adapter_blob() const { return false; } @@ -182,6 +184,9 @@ class CodeBlob VALUE_OBJ_CLASS_SPEC { virtual void print_on(outputStream* st) const; virtual void print_value_on(outputStream* st) const; + // Deal with Disassembler, VTune, Forte, JvmtiExport, MemoryService. + static void trace_new_stub(CodeBlob* blob, const char* name1, const char* name2 = ""); + // Print the comment associated with offset on stream, if there is one virtual void print_block_comment(outputStream* stream, address block_begin) { intptr_t offset = (intptr_t)(block_begin - code_begin()); @@ -318,7 +323,11 @@ class RuntimeStub: public CodeBlob { class SingletonBlob: public CodeBlob { friend class VMStructs; - public: + + protected: + void* operator new(size_t s, unsigned size); + + public: SingletonBlob( const char* name, CodeBuffer* cb, @@ -340,6 +349,50 @@ class SingletonBlob: public CodeBlob { }; +//---------------------------------------------------------------------------------------------------- +// RicochetBlob +// Holds an arbitrary argument list indefinitely while Java code executes recursively. + +class RicochetBlob: public SingletonBlob { + friend class VMStructs; + private: + + int _bounce_offset; + int _exception_offset; + + // Creation support + RicochetBlob( + CodeBuffer* cb, + int size, + int bounce_offset, + int exception_offset, + int frame_size + ); + + public: + // Creation + static RicochetBlob* create( + CodeBuffer* cb, + int bounce_offset, + int exception_offset, + int frame_size + ); + + // Typing + bool is_ricochet_stub() const { return true; } + + // GC for args + void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) { /* Nothing to do */ } + + address bounce_addr() const { return code_begin() + _bounce_offset; } + address exception_addr() const { return code_begin() + _exception_offset; } + bool returns_to_bounce_addr(address pc) const { + address bounce_pc = bounce_addr(); + return (pc == bounce_pc || (pc + frame::pc_return_offset) == bounce_pc); + } +}; + + //---------------------------------------------------------------------------------------------------- // DeoptimizationBlob @@ -363,8 +416,6 @@ class DeoptimizationBlob: public SingletonBlob { int frame_size ); - void* operator new(size_t s, unsigned size); - public: // Creation static DeoptimizationBlob* create( @@ -378,7 +429,6 @@ class DeoptimizationBlob: public SingletonBlob { // Typing bool is_deoptimization_stub() const { return true; } - const DeoptimizationBlob *as_deoptimization_stub() const { return this; } bool exception_address_is_unpack_entry(address pc) const { address unpack_pc = unpack(); return (pc == unpack_pc || (pc + frame::pc_return_offset) == unpack_pc); @@ -426,8 +476,6 @@ class UncommonTrapBlob: public SingletonBlob { int frame_size ); - void* operator new(size_t s, unsigned size); - public: // Creation static UncommonTrapBlob* create( @@ -458,8 +506,6 @@ class ExceptionBlob: public SingletonBlob { int frame_size ); - void* operator new(size_t s, unsigned size); - public: // Creation static ExceptionBlob* create( @@ -491,8 +537,6 @@ class SafepointBlob: public SingletonBlob { int frame_size ); - void* operator new(size_t s, unsigned size); - public: // Creation static SafepointBlob* create( diff --git a/hotspot/src/share/vm/code/codeCache.cpp b/hotspot/src/share/vm/code/codeCache.cpp index 2dd42dc06af..8e82aaad056 100644 --- a/hotspot/src/share/vm/code/codeCache.cpp +++ b/hotspot/src/share/vm/code/codeCache.cpp @@ -796,6 +796,7 @@ void CodeCache::print_internals() { int nmethodCount = 0; int runtimeStubCount = 0; int adapterCount = 0; + int ricochetStubCount = 0; int deoptimizationStubCount = 0; int uncommonTrapStubCount = 0; int bufferBlobCount = 0; @@ -840,6 +841,8 @@ void CodeCache::print_internals() { } } else if (cb->is_runtime_stub()) { runtimeStubCount++; + } else if (cb->is_ricochet_stub()) { + ricochetStubCount++; } else if (cb->is_deoptimization_stub()) { deoptimizationStubCount++; } else if (cb->is_uncommon_trap_stub()) { @@ -876,6 +879,7 @@ void CodeCache::print_internals() { tty->print_cr("runtime_stubs: %d",runtimeStubCount); tty->print_cr("adapters: %d",adapterCount); tty->print_cr("buffer blobs: %d",bufferBlobCount); + tty->print_cr("ricochet_stubs: %d",ricochetStubCount); tty->print_cr("deoptimization_stubs: %d",deoptimizationStubCount); tty->print_cr("uncommon_traps: %d",uncommonTrapStubCount); tty->print_cr("\nnmethod size distribution (non-zombie java)"); diff --git a/hotspot/src/share/vm/compiler/disassembler.cpp b/hotspot/src/share/vm/compiler/disassembler.cpp index 48813b6cb89..0b4eb6dd638 100644 --- a/hotspot/src/share/vm/compiler/disassembler.cpp +++ b/hotspot/src/share/vm/compiler/disassembler.cpp @@ -283,10 +283,10 @@ void decode_env::print_address(address adr) { st->print("Stub::%s", desc->name()); if (desc->begin() != adr) st->print("%+d 0x%p",adr - desc->begin(), adr); - else if (WizardMode) st->print(" " INTPTR_FORMAT, adr); + else if (WizardMode) st->print(" " PTR_FORMAT, adr); return; } - st->print("Stub:: " INTPTR_FORMAT, adr); + st->print("Stub:: " PTR_FORMAT, adr); return; } @@ -314,8 +314,8 @@ void decode_env::print_address(address adr) { } } - // Fall through to a simple numeral. - st->print(INTPTR_FORMAT, (intptr_t)adr); + // Fall through to a simple (hexadecimal) numeral. + st->print(PTR_FORMAT, adr); } void decode_env::print_insn_labels() { @@ -326,7 +326,7 @@ void decode_env::print_insn_labels() { cb->print_block_comment(st, p); } if (_print_pc) { - st->print(" " INTPTR_FORMAT ": ", (intptr_t) p); + st->print(" " PTR_FORMAT ": ", p); } } @@ -432,7 +432,7 @@ address decode_env::decode_instructions(address start, address end) { void Disassembler::decode(CodeBlob* cb, outputStream* st) { if (!load_library()) return; decode_env env(cb, st); - env.output()->print_cr("Decoding CodeBlob " INTPTR_FORMAT, cb); + env.output()->print_cr("Decoding CodeBlob " PTR_FORMAT, cb); env.decode_instructions(cb->code_begin(), cb->code_end()); } @@ -446,7 +446,7 @@ void Disassembler::decode(address start, address end, outputStream* st) { void Disassembler::decode(nmethod* nm, outputStream* st) { if (!load_library()) return; decode_env env(nm, st); - env.output()->print_cr("Decoding compiled method " INTPTR_FORMAT ":", nm); + env.output()->print_cr("Decoding compiled method " PTR_FORMAT ":", nm); env.output()->print_cr("Code:"); #ifdef SHARK @@ -478,9 +478,9 @@ void Disassembler::decode(nmethod* nm, outputStream* st) { int offset = 0; for (address p = nm->consts_begin(); p < nm->consts_end(); p += 4, offset += 4) { if ((offset % 8) == 0) { - env.output()->print_cr(" " INTPTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, (intptr_t) p, offset, *((int32_t*) p), *((int64_t*) p)); + env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, p, offset, *((int32_t*) p), *((int64_t*) p)); } else { - env.output()->print_cr(" " INTPTR_FORMAT " (offset: %4d): " PTR32_FORMAT, (intptr_t) p, offset, *((int32_t*) p)); + env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT, p, offset, *((int32_t*) p)); } } } diff --git a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp index de5803b69ee..a7caebe4b44 100644 --- a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp +++ b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp @@ -3158,6 +3158,9 @@ inline bool VM_HeapWalkOperation::collect_stack_roots(JavaThread* java_thread, if (fr->is_entry_frame()) { last_entry_frame = fr; } + if (fr->is_ricochet_frame()) { + fr->oops_ricochet_do(blk, vf->register_map()); + } } vf = vf->sender(); diff --git a/hotspot/src/share/vm/prims/methodHandleWalk.cpp b/hotspot/src/share/vm/prims/methodHandleWalk.cpp index 936d1e73952..281934cba46 100644 --- a/hotspot/src/share/vm/prims/methodHandleWalk.cpp +++ b/hotspot/src/share/vm/prims/methodHandleWalk.cpp @@ -409,6 +409,11 @@ MethodHandleWalker::walk(TRAPS) { break; } + case java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS: { //NYI, may GC + lose("unimplemented", CHECK_(empty)); + break; + } + case java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS: { klassOop array_klass_oop = NULL; BasicType array_type = java_lang_Class::as_BasicType(chain().adapter_arg_oop(), @@ -452,9 +457,18 @@ MethodHandleWalker::walk(TRAPS) { Bytecodes::_invokestatic, false, 3, &arglist[0], CHECK_(empty)); // Spread out the array elements. - Bytecodes::Code aload_op = Bytecodes::_aaload; - if (element_type != T_OBJECT) { - lose("primitive array NYI", CHECK_(empty)); + Bytecodes::Code aload_op = Bytecodes::_nop; + switch (element_type) { + case T_INT: aload_op = Bytecodes::_iaload; break; + case T_LONG: aload_op = Bytecodes::_laload; break; + case T_FLOAT: aload_op = Bytecodes::_faload; break; + case T_DOUBLE: aload_op = Bytecodes::_daload; break; + case T_OBJECT: aload_op = Bytecodes::_aaload; break; + case T_BOOLEAN: // fall through: + case T_BYTE: aload_op = Bytecodes::_baload; break; + case T_CHAR: aload_op = Bytecodes::_caload; break; + case T_SHORT: aload_op = Bytecodes::_saload; break; + default: lose("primitive array NYI", CHECK_(empty)); } int ap = arg_slot; for (int i = 0; i < spread_length; i++) { @@ -467,11 +481,6 @@ MethodHandleWalker::walk(TRAPS) { break; } - case java_lang_invoke_AdapterMethodHandle::OP_FLYBY: //NYI, runs Java code - case java_lang_invoke_AdapterMethodHandle::OP_RICOCHET: //NYI, runs Java code - lose("unimplemented", CHECK_(empty)); - break; - default: lose("bad adapter conversion", CHECK_(empty)); break; diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp index 12a483f23b5..f50ce0e57ea 100644 --- a/hotspot/src/share/vm/prims/methodHandles.cpp +++ b/hotspot/src/share/vm/prims/methodHandles.cpp @@ -66,8 +66,8 @@ const char* MethodHandles::_entry_names[_EK_LIMIT+1] = { "adapter_drop_args", "adapter_collect_args", "adapter_spread_args", - "adapter_flyby", - "adapter_ricochet", + "adapter_fold_args", + "adapter_unused_13", // optimized adapter types: "adapter_swap_args/1", @@ -83,9 +83,76 @@ const char* MethodHandles::_entry_names[_EK_LIMIT+1] = { "adapter_prim_to_prim/f2d", "adapter_ref_to_prim/unboxi", "adapter_ref_to_prim/unboxl", - "adapter_spread_args/0", - "adapter_spread_args/1", - "adapter_spread_args/more", + + // return value handlers for collect/filter/fold adapters: + "return/ref", + "return/int", + "return/long", + "return/float", + "return/double", + "return/void", + "return/S0/ref", + "return/S1/ref", + "return/S2/ref", + "return/S3/ref", + "return/S4/ref", + "return/S5/ref", + "return/any", + + // spreading (array length cases 0, 1, ...) + "adapter_spread/0", + "adapter_spread/1/ref", + "adapter_spread/2/ref", + "adapter_spread/3/ref", + "adapter_spread/4/ref", + "adapter_spread/5/ref", + "adapter_spread/ref", + "adapter_spread/byte", + "adapter_spread/char", + "adapter_spread/short", + "adapter_spread/int", + "adapter_spread/long", + "adapter_spread/float", + "adapter_spread/double", + + // blocking filter/collect conversions: + "adapter_collect/ref", + "adapter_collect/int", + "adapter_collect/long", + "adapter_collect/float", + "adapter_collect/double", + "adapter_collect/void", + "adapter_collect/0/ref", + "adapter_collect/1/ref", + "adapter_collect/2/ref", + "adapter_collect/3/ref", + "adapter_collect/4/ref", + "adapter_collect/5/ref", + "adapter_filter/S0/ref", + "adapter_filter/S1/ref", + "adapter_filter/S2/ref", + "adapter_filter/S3/ref", + "adapter_filter/S4/ref", + "adapter_filter/S5/ref", + "adapter_collect/2/S0/ref", + "adapter_collect/2/S1/ref", + "adapter_collect/2/S2/ref", + "adapter_collect/2/S3/ref", + "adapter_collect/2/S4/ref", + "adapter_collect/2/S5/ref", + + // blocking fold conversions: + "adapter_fold/ref", + "adapter_fold/int", + "adapter_fold/long", + "adapter_fold/float", + "adapter_fold/double", + "adapter_fold/void", + "adapter_fold/1/ref", + "adapter_fold/2/ref", + "adapter_fold/3/ref", + "adapter_fold/4/ref", + "adapter_fold/5/ref", NULL }; @@ -96,13 +163,23 @@ int MethodHandles::_adapter_code_size = StubRoutines::meth jobject MethodHandles::_raise_exception_method; +address MethodHandles::_adapter_return_handlers[CONV_TYPE_MASK+1]; + #ifdef ASSERT bool MethodHandles::spot_check_entry_names() { assert(!strcmp(entry_name(_invokestatic_mh), "invokestatic"), ""); assert(!strcmp(entry_name(_bound_ref_mh), "bound_ref"), ""); assert(!strcmp(entry_name(_adapter_retype_only), "adapter_retype_only"), ""); - assert(!strcmp(entry_name(_adapter_ricochet), "adapter_ricochet"), ""); + assert(!strcmp(entry_name(_adapter_fold_args), "adapter_fold_args"), ""); assert(!strcmp(entry_name(_adapter_opt_unboxi), "adapter_ref_to_prim/unboxi"), ""); + assert(!strcmp(entry_name(_adapter_opt_spread_char), "adapter_spread/char"), ""); + assert(!strcmp(entry_name(_adapter_opt_spread_double), "adapter_spread/double"), ""); + assert(!strcmp(entry_name(_adapter_opt_collect_int), "adapter_collect/int"), ""); + assert(!strcmp(entry_name(_adapter_opt_collect_0_ref), "adapter_collect/0/ref"), ""); + assert(!strcmp(entry_name(_adapter_opt_collect_2_S3_ref), "adapter_collect/2/S3/ref"), ""); + assert(!strcmp(entry_name(_adapter_opt_filter_S5_ref), "adapter_filter/S5/ref"), ""); + assert(!strcmp(entry_name(_adapter_opt_fold_3_ref), "adapter_fold/3/ref"), ""); + assert(!strcmp(entry_name(_adapter_opt_fold_void), "adapter_fold/void"), ""); return true; } #endif @@ -112,6 +189,9 @@ bool MethodHandles::spot_check_entry_names() { // MethodHandles::generate_adapters // void MethodHandles::generate_adapters() { +#ifdef TARGET_ARCH_NYI_6939861 + if (FLAG_IS_DEFAULT(UseRicochetFrames)) UseRicochetFrames = false; +#endif if (!EnableInvokeDynamic || SystemDictionary::MethodHandle_klass() == NULL) return; assert(_adapter_code == NULL, "generate only once"); @@ -126,7 +206,6 @@ void MethodHandles::generate_adapters() { g.generate(); } - //------------------------------------------------------------------------------ // MethodHandlesAdapterGenerator::generate // @@ -135,12 +214,62 @@ void MethodHandlesAdapterGenerator::generate() { for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST; ek < MethodHandles::_EK_LIMIT; ek = MethodHandles::EntryKind(1 + (int)ek)) { - StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek)); - MethodHandles::generate_method_handle_stub(_masm, ek); + if (MethodHandles::ek_supported(ek)) { + StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek)); + MethodHandles::generate_method_handle_stub(_masm, ek); + } } } +#ifdef TARGET_ARCH_NYI_6939861 +// these defs belong in methodHandles_.cpp +frame MethodHandles::ricochet_frame_sender(const frame& fr, RegisterMap *map) { + ShouldNotCallThis(); + return fr; +} +void MethodHandles::ricochet_frame_oops_do(const frame& fr, OopClosure* f, const RegisterMap* reg_map) { + ShouldNotCallThis(); +} +#endif //TARGET_ARCH_NYI_6939861 + + +//------------------------------------------------------------------------------ +// MethodHandles::ek_supported +// +bool MethodHandles::ek_supported(MethodHandles::EntryKind ek) { + MethodHandles::EntryKind ek_orig = MethodHandles::ek_original_kind(ek); + switch (ek_orig) { + case _adapter_unused_13: + return false; // not defined yet + case _adapter_prim_to_ref: + return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF); + case _adapter_collect_args: + return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS); + case _adapter_fold_args: + return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS); + case _adapter_opt_return_any: + return UseRicochetFrames; +#ifdef TARGET_ARCH_NYI_6939861 + // ports before 6939861 supported only three kinds of spread ops + case _adapter_spread_args: + // restrict spreads to three kinds: + switch (ek) { + case _adapter_opt_spread_0: + case _adapter_opt_spread_1: + case _adapter_opt_spread_more: + break; + default: + return false; + break; + } + break; +#endif //TARGET_ARCH_NYI_6939861 + } + return true; +} + + void MethodHandles::set_enabled(bool z) { if (_enabled != z) { guarantee(z && EnableInvokeDynamic, "can only enable once, and only if -XX:+EnableInvokeDynamic"); @@ -1564,6 +1693,8 @@ void MethodHandles::init_BoundMethodHandle_with_receiver(Handle mh, if (m->is_abstract()) { THROW(vmSymbols::java_lang_AbstractMethodError()); } java_lang_invoke_MethodHandle::init_vmslots(mh()); + int vmargslot = m->size_of_parameters() - 1; + assert(java_lang_invoke_BoundMethodHandle::vmargslot(mh()) == vmargslot, ""); if (VerifyMethodHandles) { verify_BoundMethodHandle_with_receiver(mh, m, CHECK); @@ -1642,14 +1773,9 @@ void MethodHandles::verify_BoundMethodHandle(Handle mh, Handle target, int argnu DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh())); if (direct_to_method) { assert(this_pushes == slots_pushed, "BMH pushes one or two stack slots"); - assert(slots_pushed <= MethodHandlePushLimit, ""); } else { int target_pushes = decode_MethodHandle_stack_pushes(target()); assert(this_pushes == slots_pushed + target_pushes, "BMH stack motion must be correct"); - // do not blow the stack; use a Java-based adapter if this limit is exceeded - // FIXME - // if (slots_pushed + target_pushes > MethodHandlePushLimit) - // err = "too many bound parameters"; } } @@ -1672,10 +1798,11 @@ void MethodHandles::init_BoundMethodHandle(Handle mh, Handle target, int argnum, } java_lang_invoke_MethodHandle::init_vmslots(mh()); + int argslot = java_lang_invoke_BoundMethodHandle::vmargslot(mh()); if (VerifyMethodHandles) { int insert_after = argnum - 1; - verify_vmargslot(mh, insert_after, java_lang_invoke_BoundMethodHandle::vmargslot(mh()), CHECK); + verify_vmargslot(mh, insert_after, argslot, CHECK); verify_vmslots(mh, CHECK); } @@ -1769,6 +1896,7 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { Handle target(THREAD, java_lang_invoke_AdapterMethodHandle::vmtarget(mh())); Handle src_mtype(THREAD, java_lang_invoke_MethodHandle::type(mh())); Handle dst_mtype(THREAD, java_lang_invoke_MethodHandle::type(target())); + Handle arg_mtype; const char* err = NULL; @@ -1777,25 +1905,29 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { switch (ek) { case _adapter_check_cast: // target type of cast case _adapter_ref_to_prim: // wrapper type from which to unbox - case _adapter_prim_to_ref: // wrapper type to box into - case _adapter_collect_args: // array type to collect into case _adapter_spread_args: // array type to spread from if (!java_lang_Class::is_instance(argument()) || java_lang_Class::is_primitive(argument())) { err = "adapter requires argument of type java.lang.Class"; break; } - if (ek == _adapter_collect_args || - ek == _adapter_spread_args) { + if (ek == _adapter_spread_args) { // Make sure it is a suitable collection type. (Array, for now.) Klass* ak = Klass::cast(java_lang_Class::as_klassOop(argument())); - if (!ak->oop_is_objArray()) { - { err = "adapter requires argument of type java.lang.Class"; break; } - } + if (!ak->oop_is_array()) + { err = "spread adapter requires argument representing an array class"; break; } + BasicType et = arrayKlass::cast(ak->as_klassOop())->element_type(); + if (et != dest && stack_move <= 0) + { err = "spread adapter requires array class argument of correct type"; break; } } break; - case _adapter_flyby: - case _adapter_ricochet: + case _adapter_prim_to_ref: // boxer MH to use + case _adapter_collect_args: // method handle which collects the args + case _adapter_fold_args: // method handle which collects the args + if (!UseRicochetFrames) { + { err = "box/collect/fold operators are not supported"; break; } + } if (!java_lang_invoke_MethodHandle::is_instance(argument())) { err = "MethodHandle adapter argument required"; break; } + arg_mtype = Handle(THREAD, java_lang_invoke_MethodHandle::type(argument())); break; default: if (argument.not_null()) @@ -1806,6 +1938,7 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { if (err == NULL) { // Check that the src/dest types are supplied if needed. + // Also check relevant parameter or return types. switch (ek) { case _adapter_check_cast: if (src != T_OBJECT || dest != T_OBJECT) { @@ -1828,8 +1961,7 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { } break; case _adapter_prim_to_ref: - if (!is_java_primitive(src) || dest != T_OBJECT - || argument() != Klass::cast(SystemDictionary::box_klass(src))->java_mirror()) { + if (!is_java_primitive(src) || dest != T_OBJECT) { err = "adapter requires primitive src conversion subfield"; break; } break; @@ -1840,14 +1972,12 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { err = "adapter requires src/dest conversion subfields for swap"; break; } int swap_size = type2size[src]; - oop src_mtype = java_lang_invoke_AdapterMethodHandle::type(mh()); - oop dest_mtype = java_lang_invoke_AdapterMethodHandle::type(target()); - int slot_limit = java_lang_invoke_AdapterMethodHandle::vmslots(target()); + int slot_limit = java_lang_invoke_MethodHandle::vmslots(target()); int src_slot = argslot; int dest_slot = vminfo; bool rotate_up = (src_slot > dest_slot); // upward rotation int src_arg = argnum; - int dest_arg = argument_slot_to_argnum(dest_mtype, dest_slot); + int dest_arg = argument_slot_to_argnum(dst_mtype(), dest_slot); verify_vmargslot(mh, dest_arg, dest_slot, CHECK); if (!(dest_slot >= src_slot + swap_size) && !(src_slot >= dest_slot + swap_size)) { @@ -1855,8 +1985,8 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { } else if (ek == _adapter_swap_args && !(src_slot > dest_slot)) { err = "source of swap must be deeper in stack"; } else if (ek == _adapter_swap_args) { - err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, dest_arg), - java_lang_invoke_MethodType::ptype(dest_mtype, src_arg), + err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), dest_arg), + java_lang_invoke_MethodType::ptype(dst_mtype(), src_arg), dest_arg); } else if (ek == _adapter_rot_args) { if (rotate_up) { @@ -1864,8 +1994,8 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { // rotate up: [dest_slot..src_slot-ss] --> [dest_slot+ss..src_slot] // that is: [src_arg+1..dest_arg] --> [src_arg..dest_arg-1] for (int i = src_arg+1; i <= dest_arg && err == NULL; i++) { - err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, i), - java_lang_invoke_MethodType::ptype(dest_mtype, i-1), + err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), i), + java_lang_invoke_MethodType::ptype(dst_mtype(), i-1), i); } } else { // rotate down @@ -1873,28 +2003,54 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { // rotate down: [src_slot+ss..dest_slot] --> [src_slot..dest_slot-ss] // that is: [dest_arg..src_arg-1] --> [dst_arg+1..src_arg] for (int i = dest_arg; i <= src_arg-1 && err == NULL; i++) { - err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, i), - java_lang_invoke_MethodType::ptype(dest_mtype, i+1), + err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), i), + java_lang_invoke_MethodType::ptype(dst_mtype(), i+1), i); } } } if (err == NULL) - err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, src_arg), - java_lang_invoke_MethodType::ptype(dest_mtype, dest_arg), + err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), src_arg), + java_lang_invoke_MethodType::ptype(dst_mtype(), dest_arg), src_arg); } break; - case _adapter_collect_args: case _adapter_spread_args: + case _adapter_collect_args: + case _adapter_fold_args: { - BasicType coll_type = (ek == _adapter_collect_args) ? dest : src; - BasicType elem_type = (ek == _adapter_collect_args) ? src : dest; - if (coll_type != T_OBJECT || elem_type != T_OBJECT) { - err = "adapter requires src/dest subfields"; break; - // later: - // - consider making coll be a primitive array - // - consider making coll be a heterogeneous collection + bool is_spread = (ek == _adapter_spread_args); + bool is_fold = (ek == _adapter_fold_args); + BasicType coll_type = is_spread ? src : dest; + BasicType elem_type = is_spread ? dest : src; + // coll_type is type of args in collected form (or T_VOID if none) + // elem_type is common type of args in spread form (or T_VOID if missing or heterogeneous) + if (coll_type == 0 || elem_type == 0) { + err = "adapter requires src/dest subfields for spread or collect"; break; + } + if (is_spread && coll_type != T_OBJECT) { + err = "spread adapter requires object type for argument bundle"; break; + } + Handle spread_mtype = (is_spread ? dst_mtype : src_mtype); + int spread_slot = argslot; + int spread_arg = argnum; + int slots_pushed = stack_move / stack_move_unit(); + int coll_slot_count = type2size[coll_type]; + int spread_slot_count = (is_spread ? slots_pushed : -slots_pushed) + coll_slot_count; + if (is_fold) spread_slot_count = argument_slot_count(arg_mtype()); + if (!is_spread) { + int init_slots = argument_slot_count(src_mtype()); + int coll_slots = argument_slot_count(arg_mtype()); + if (spread_slot_count > init_slots || + spread_slot_count != coll_slots) { + err = "collect adapter has inconsistent arg counts"; break; + } + int next_slots = argument_slot_count(dst_mtype()); + int unchanged_slots_in = (init_slots - spread_slot_count); + int unchanged_slots_out = (next_slots - coll_slot_count - (is_fold ? spread_slot_count : 0)); + if (unchanged_slots_in != unchanged_slots_out) { + err = "collect adapter continuation has inconsistent arg counts"; break; + } } } break; @@ -1929,8 +2085,9 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { } break; case _adapter_collect_args: - if (slots_pushed > 1) { - err = "adapter requires conversion subfield slots_pushed <= 1"; + case _adapter_fold_args: + if (slots_pushed > 2) { + err = "adapter requires conversion subfield slots_pushed <= 2"; } break; case _adapter_spread_args: @@ -1950,32 +2107,36 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { } if (err == NULL) { - // Make sure this adapter does not push too deeply. + // Make sure this adapter's stack pushing is accurately recorded. int slots_pushed = stack_move / stack_move_unit(); int this_vmslots = java_lang_invoke_MethodHandle::vmslots(mh()); int target_vmslots = java_lang_invoke_MethodHandle::vmslots(target()); + int target_pushes = decode_MethodHandle_stack_pushes(target()); if (slots_pushed != (target_vmslots - this_vmslots)) { err = "stack_move inconsistent with previous and current MethodType vmslots"; - } else if (slots_pushed > 0) { - // verify stack_move against MethodHandlePushLimit - int target_pushes = decode_MethodHandle_stack_pushes(target()); - // do not blow the stack; use a Java-based adapter if this limit is exceeded - if (slots_pushed + target_pushes > MethodHandlePushLimit) { - err = "adapter pushes too many parameters"; + } else { + int this_pushes = decode_MethodHandle_stack_pushes(mh()); + if (slots_pushed + target_pushes != this_pushes) { + if (this_pushes == 0) + err = "adapter push count not initialized"; + else + err = "adapter push count is wrong"; } } // While we're at it, check that the stack motion decoder works: - DEBUG_ONLY(int target_pushes = decode_MethodHandle_stack_pushes(target())); DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh())); assert(this_pushes == slots_pushed + target_pushes, "AMH stack motion must be correct"); } if (err == NULL && vminfo != 0) { switch (ek) { - case _adapter_swap_args: - case _adapter_rot_args: - break; // OK + case _adapter_swap_args: + case _adapter_rot_args: + case _adapter_prim_to_ref: + case _adapter_collect_args: + case _adapter_fold_args: + break; // OK default: err = "vminfo subfield is reserved to the JVM"; } @@ -2026,6 +2187,7 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu // adjust the adapter code to the internal EntryKind enumeration: EntryKind ek_orig = adapter_entry_kind(conv_op); EntryKind ek_opt = ek_orig; // may be optimized + EntryKind ek_try; // temp // Finalize the vmtarget field (Java initialized it to null). if (!java_lang_invoke_MethodHandle::is_instance(target())) { @@ -2034,17 +2196,23 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu } java_lang_invoke_AdapterMethodHandle::set_vmtarget(mh(), target()); - if (VerifyMethodHandles) { - verify_AdapterMethodHandle(mh, argnum, CHECK); - } - int stack_move = adapter_conversion_stack_move(conversion); BasicType src = adapter_conversion_src_type(conversion); BasicType dest = adapter_conversion_dest_type(conversion); int vminfo = adapter_conversion_vminfo(conversion); // should be zero + int slots_pushed = stack_move / stack_move_unit(); + + if (VerifyMethodHandles) { + verify_AdapterMethodHandle(mh, argnum, CHECK); + } + const char* err = NULL; + if (!conv_op_supported(conv_op)) { + err = "adapter not yet implemented in the JVM"; + } + // Now it's time to finish the case analysis and pick a MethodHandleEntry. switch (ek_orig) { case _adapter_retype_only: @@ -2077,7 +2245,7 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu } break; case 1 *4+ 2: - if (src == T_INT && dest == T_LONG) { + if ((src == T_INT || is_subword_type(src)) && dest == T_LONG) { ek_opt = _adapter_opt_i2l; } else if (src == T_FLOAT && dest == T_DOUBLE) { ek_opt = _adapter_opt_f2d; @@ -2110,7 +2278,44 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu break; case _adapter_prim_to_ref: - goto throw_not_impl; // allocates, hence could block + { + assert(UseRicochetFrames, "else don't come here"); + // vminfo will be the location to insert the return value + vminfo = argslot; + ek_opt = _adapter_opt_collect_ref; + ensure_vmlayout_field(target, CHECK); + if (!OptimizeMethodHandles) break; + switch (type2size[src]) { + case 1: + ek_try = EntryKind(_adapter_opt_filter_S0_ref + argslot); + if (ek_try < _adapter_opt_collect_LAST && + ek_adapter_opt_collect_slot(ek_try) == argslot) { + assert(ek_adapter_opt_collect_count(ek_try) == 1 && + ek_adapter_opt_collect_type(ek_try) == T_OBJECT, ""); + ek_opt = ek_try; + break; + } + // else downgrade to variable slot: + ek_opt = _adapter_opt_collect_1_ref; + break; + case 2: + ek_try = EntryKind(_adapter_opt_collect_2_S0_ref + argslot); + if (ek_try < _adapter_opt_collect_LAST && + ek_adapter_opt_collect_slot(ek_try) == argslot) { + assert(ek_adapter_opt_collect_count(ek_try) == 2 && + ek_adapter_opt_collect_type(ek_try) == T_OBJECT, ""); + ek_opt = ek_try; + break; + } + // else downgrade to variable slot: + ek_opt = _adapter_opt_collect_2_ref; + break; + default: + assert(false, ""); + break; + } + } + break; case _adapter_swap_args: case _adapter_rot_args: @@ -2136,29 +2341,180 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu } break; - case _adapter_collect_args: - goto throw_not_impl; // allocates, hence could block - case _adapter_spread_args: { - // vminfo will be the required length of the array - int slots_pushed = stack_move / stack_move_unit(); - int array_size = slots_pushed + 1; - assert(array_size >= 0, ""); - vminfo = array_size; - switch (array_size) { - case 0: ek_opt = _adapter_opt_spread_0; break; - case 1: ek_opt = _adapter_opt_spread_1; break; - default: ek_opt = _adapter_opt_spread_more; break; +#ifdef TARGET_ARCH_NYI_6939861 + // ports before 6939861 supported only three kinds of spread ops + if (!UseRicochetFrames) { + int array_size = slots_pushed + 1; + assert(array_size >= 0, ""); + vminfo = array_size; + switch (array_size) { + case 0: ek_opt = _adapter_opt_spread_0; break; + case 1: ek_opt = _adapter_opt_spread_1; break; + default: ek_opt = _adapter_opt_spread_more; break; + } + break; } - if ((vminfo & CONV_VMINFO_MASK) != vminfo) - goto throw_not_impl; // overflow +#endif //TARGET_ARCH_NYI_6939861 + // vminfo will be the required length of the array + int array_size = (slots_pushed + 1) / (type2size[dest] == 2 ? 2 : 1); + vminfo = array_size; + // general case + switch (dest) { + case T_BOOLEAN : // fall through to T_BYTE: + case T_BYTE : ek_opt = _adapter_opt_spread_byte; break; + case T_CHAR : ek_opt = _adapter_opt_spread_char; break; + case T_SHORT : ek_opt = _adapter_opt_spread_short; break; + case T_INT : ek_opt = _adapter_opt_spread_int; break; + case T_LONG : ek_opt = _adapter_opt_spread_long; break; + case T_FLOAT : ek_opt = _adapter_opt_spread_float; break; + case T_DOUBLE : ek_opt = _adapter_opt_spread_double; break; + case T_OBJECT : ek_opt = _adapter_opt_spread_ref; break; + case T_VOID : if (array_size != 0) goto throw_not_impl; + ek_opt = _adapter_opt_spread_ref; break; + default : goto throw_not_impl; + } + assert(array_size == 0 || // it doesn't matter what the spreader is + (ek_adapter_opt_spread_count(ek_opt) == -1 && + (ek_adapter_opt_spread_type(ek_opt) == dest || + (ek_adapter_opt_spread_type(ek_opt) == T_BYTE && dest == T_BOOLEAN))), + err_msg("dest=%d ek_opt=%d", dest, ek_opt)); + + if (array_size <= 0) { + // since the general case does not handle length 0, this case is required: + ek_opt = _adapter_opt_spread_0; + break; + } + if (dest == T_OBJECT) { + ek_try = EntryKind(_adapter_opt_spread_1_ref - 1 + array_size); + if (ek_try < _adapter_opt_spread_LAST && + ek_adapter_opt_spread_count(ek_try) == array_size) { + assert(ek_adapter_opt_spread_type(ek_try) == dest, ""); + ek_opt = ek_try; + break; + } + } + break; } break; - case _adapter_flyby: - case _adapter_ricochet: - goto throw_not_impl; // runs Java code, hence could block + case _adapter_collect_args: + { + assert(UseRicochetFrames, "else don't come here"); + int elem_slots = argument_slot_count( + java_lang_invoke_MethodHandle::type( + java_lang_invoke_AdapterMethodHandle::argument(mh()) ) ); + // vminfo will be the location to insert the return value + vminfo = argslot; + ensure_vmlayout_field(target, CHECK); + + // general case: + switch (dest) { + default : if (!is_subword_type(dest)) goto throw_not_impl; + // else fall through: + case T_INT : ek_opt = _adapter_opt_collect_int; break; + case T_LONG : ek_opt = _adapter_opt_collect_long; break; + case T_FLOAT : ek_opt = _adapter_opt_collect_float; break; + case T_DOUBLE : ek_opt = _adapter_opt_collect_double; break; + case T_OBJECT : ek_opt = _adapter_opt_collect_ref; break; + case T_VOID : ek_opt = _adapter_opt_collect_void; break; + } + assert(ek_adapter_opt_collect_slot(ek_opt) == -1 && + ek_adapter_opt_collect_count(ek_opt) == -1 && + (ek_adapter_opt_collect_type(ek_opt) == dest || + ek_adapter_opt_collect_type(ek_opt) == T_INT && is_subword_type(dest)), + ""); + + if (dest == T_OBJECT && elem_slots == 1 && OptimizeMethodHandles) { + // filter operation on a ref + ek_try = EntryKind(_adapter_opt_filter_S0_ref + argslot); + if (ek_try < _adapter_opt_collect_LAST && + ek_adapter_opt_collect_slot(ek_try) == argslot) { + assert(ek_adapter_opt_collect_count(ek_try) == elem_slots && + ek_adapter_opt_collect_type(ek_try) == dest, ""); + ek_opt = ek_try; + break; + } + ek_opt = _adapter_opt_collect_1_ref; + break; + } + + if (dest == T_OBJECT && elem_slots == 2 && OptimizeMethodHandles) { + // filter of two arguments + ek_try = EntryKind(_adapter_opt_collect_2_S0_ref + argslot); + if (ek_try < _adapter_opt_collect_LAST && + ek_adapter_opt_collect_slot(ek_try) == argslot) { + assert(ek_adapter_opt_collect_count(ek_try) == elem_slots && + ek_adapter_opt_collect_type(ek_try) == dest, ""); + ek_opt = ek_try; + break; + } + ek_opt = _adapter_opt_collect_2_ref; + break; + } + + if (dest == T_OBJECT && OptimizeMethodHandles) { + // try to use a fixed length adapter + ek_try = EntryKind(_adapter_opt_collect_0_ref + elem_slots); + if (ek_try < _adapter_opt_collect_LAST && + ek_adapter_opt_collect_count(ek_try) == elem_slots) { + assert(ek_adapter_opt_collect_slot(ek_try) == -1 && + ek_adapter_opt_collect_type(ek_try) == dest, ""); + ek_opt = ek_try; + break; + } + } + + break; + } + + case _adapter_fold_args: + { + assert(UseRicochetFrames, "else don't come here"); + int elem_slots = argument_slot_count( + java_lang_invoke_MethodHandle::type( + java_lang_invoke_AdapterMethodHandle::argument(mh()) ) ); + // vminfo will be the location to insert the return value + vminfo = argslot + elem_slots; + ensure_vmlayout_field(target, CHECK); + + switch (dest) { + default : if (!is_subword_type(dest)) goto throw_not_impl; + // else fall through: + case T_INT : ek_opt = _adapter_opt_fold_int; break; + case T_LONG : ek_opt = _adapter_opt_fold_long; break; + case T_FLOAT : ek_opt = _adapter_opt_fold_float; break; + case T_DOUBLE : ek_opt = _adapter_opt_fold_double; break; + case T_OBJECT : ek_opt = _adapter_opt_fold_ref; break; + case T_VOID : ek_opt = _adapter_opt_fold_void; break; + } + assert(ek_adapter_opt_collect_slot(ek_opt) == -1 && + ek_adapter_opt_collect_count(ek_opt) == -1 && + (ek_adapter_opt_collect_type(ek_opt) == dest || + ek_adapter_opt_collect_type(ek_opt) == T_INT && is_subword_type(dest)), + ""); + + if (dest == T_OBJECT && elem_slots == 0 && OptimizeMethodHandles) { + // if there are no args, just pretend it's a collect + ek_opt = _adapter_opt_collect_0_ref; + break; + } + + if (dest == T_OBJECT && OptimizeMethodHandles) { + // try to use a fixed length adapter + ek_try = EntryKind(_adapter_opt_fold_1_ref - 1 + elem_slots); + if (ek_try < _adapter_opt_fold_LAST && + ek_adapter_opt_collect_count(ek_try) == elem_slots) { + assert(ek_adapter_opt_collect_slot(ek_try) == -1 && + ek_adapter_opt_collect_type(ek_try) == dest, ""); + ek_opt = ek_try; + break; + } + } + + break; + } default: // should have failed much earlier; must be a missing case here @@ -2166,11 +2522,20 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu // and fall through: throw_not_impl: - // FIXME: these adapters are NYI - err = "adapter not yet implemented in the JVM"; + if (err == NULL) + err = "unknown adapter type"; break; } + if (err != NULL && (vminfo & CONV_VMINFO_MASK) != vminfo) { + // should not happen, since vminfo is used to encode arg/slot indexes < 255 + err = "vminfo overflow"; + } + + if (err != NULL && !have_entry(ek_opt)) { + err = "adapter stub for this kind of method handle is missing"; + } + if (err != NULL) { throw_InternalError_for_bad_conversion(conversion, err, THREAD); return; @@ -2190,6 +2555,26 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu // Java code can publish it in global data structures. } +void MethodHandles::ensure_vmlayout_field(Handle target, TRAPS) { + Handle mtype(THREAD, java_lang_invoke_MethodHandle::type(target())); + Handle mtform(THREAD, java_lang_invoke_MethodType::form(mtype())); + if (mtform.is_null()) { THROW(vmSymbols::java_lang_InternalError()); } + if (java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() > 0) { + if (java_lang_invoke_MethodTypeForm::vmlayout(mtform()) == NULL) { + // fill it in + Handle erased_mtype(THREAD, java_lang_invoke_MethodTypeForm::erasedType(mtform())); + TempNewSymbol erased_signature + = java_lang_invoke_MethodType::as_signature(erased_mtype(), /*intern:*/true, CHECK); + methodOop cookie + = SystemDictionary::find_method_handle_invoke(vmSymbols::invokeExact_name(), + erased_signature, + SystemDictionaryHandles::Object_klass(), + THREAD); + java_lang_invoke_MethodTypeForm::init_vmlayout(mtform(), cookie); + } + } +} + // // Here are the native methods on sun.invoke.MethodHandleImpl. // They are the private interface between this JVM and the HotSpot-specific @@ -2360,8 +2745,10 @@ JVM_END #ifndef PRODUCT #define EACH_NAMED_CON(template) \ - template(MethodHandles,GC_JVM_PUSH_LIMIT) \ - template(MethodHandles,GC_JVM_STACK_MOVE_UNIT) \ + /* hold back this one until JDK stabilizes */ \ + /* template(MethodHandles,GC_JVM_PUSH_LIMIT) */ \ + /* hold back this one until JDK stabilizes */ \ + /* template(MethodHandles,GC_JVM_STACK_MOVE_UNIT) */ \ template(MethodHandles,ETF_HANDLE_OR_METHOD_NAME) \ template(MethodHandles,ETF_DIRECT_HANDLE) \ template(MethodHandles,ETF_METHOD_NAME) \ @@ -2385,9 +2772,8 @@ JVM_END template(java_lang_invoke_AdapterMethodHandle,OP_DROP_ARGS) \ template(java_lang_invoke_AdapterMethodHandle,OP_COLLECT_ARGS) \ template(java_lang_invoke_AdapterMethodHandle,OP_SPREAD_ARGS) \ - template(java_lang_invoke_AdapterMethodHandle,OP_FLYBY) \ - template(java_lang_invoke_AdapterMethodHandle,OP_RICOCHET) \ - template(java_lang_invoke_AdapterMethodHandle,CONV_OP_LIMIT) \ + /* hold back this one until JDK stabilizes */ \ + /*template(java_lang_invoke_AdapterMethodHandle,CONV_OP_LIMIT)*/ \ template(java_lang_invoke_AdapterMethodHandle,CONV_OP_MASK) \ template(java_lang_invoke_AdapterMethodHandle,CONV_VMINFO_MASK) \ template(java_lang_invoke_AdapterMethodHandle,CONV_VMINFO_SHIFT) \ diff --git a/hotspot/src/share/vm/prims/methodHandles.hpp b/hotspot/src/share/vm/prims/methodHandles.hpp index d104783d40f..537054989bd 100644 --- a/hotspot/src/share/vm/prims/methodHandles.hpp +++ b/hotspot/src/share/vm/prims/methodHandles.hpp @@ -66,8 +66,8 @@ class MethodHandles: AllStatic { _adapter_drop_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS, _adapter_collect_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS, _adapter_spread_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS, - _adapter_flyby = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_FLYBY, - _adapter_ricochet = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_RICOCHET, + _adapter_fold_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS, + _adapter_unused_13 = _adapter_mh_first + 13, //hole in the CONV_OP enumeration _adapter_mh_last = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::CONV_OP_LIMIT - 1, // Optimized adapter types @@ -93,10 +93,99 @@ class MethodHandles: AllStatic { _adapter_opt_unboxi, _adapter_opt_unboxl, - // spreading (array length cases 0, 1, >=2) - _adapter_opt_spread_0, - _adapter_opt_spread_1, - _adapter_opt_spread_more, + // %% Maybe tame the following with a VM_SYMBOLS_DO type macro? + + // how a blocking adapter returns (platform-dependent) + _adapter_opt_return_ref, + _adapter_opt_return_int, + _adapter_opt_return_long, + _adapter_opt_return_float, + _adapter_opt_return_double, + _adapter_opt_return_void, + _adapter_opt_return_S0_ref, // return ref to S=0 (last slot) + _adapter_opt_return_S1_ref, // return ref to S=1 (2nd-to-last slot) + _adapter_opt_return_S2_ref, + _adapter_opt_return_S3_ref, + _adapter_opt_return_S4_ref, + _adapter_opt_return_S5_ref, + _adapter_opt_return_any, // dynamically select r/i/l/f/d + _adapter_opt_return_FIRST = _adapter_opt_return_ref, + _adapter_opt_return_LAST = _adapter_opt_return_any, + + // spreading (array length cases 0, 1, ...) + _adapter_opt_spread_0, // spread empty array to N=0 arguments + _adapter_opt_spread_1_ref, // spread Object[] to N=1 argument + _adapter_opt_spread_2_ref, // spread Object[] to N=2 arguments + _adapter_opt_spread_3_ref, // spread Object[] to N=3 arguments + _adapter_opt_spread_4_ref, // spread Object[] to N=4 arguments + _adapter_opt_spread_5_ref, // spread Object[] to N=5 arguments + _adapter_opt_spread_ref, // spread Object[] to N arguments + _adapter_opt_spread_byte, // spread byte[] or boolean[] to N arguments + _adapter_opt_spread_char, // spread char[], etc., to N arguments + _adapter_opt_spread_short, // spread short[], etc., to N arguments + _adapter_opt_spread_int, // spread int[], short[], etc., to N arguments + _adapter_opt_spread_long, // spread long[] to N arguments + _adapter_opt_spread_float, // spread float[] to N arguments + _adapter_opt_spread_double, // spread double[] to N arguments + _adapter_opt_spread_FIRST = _adapter_opt_spread_0, + _adapter_opt_spread_LAST = _adapter_opt_spread_double, + + // blocking filter/collect conversions + // These collect N arguments and replace them (at slot S) by a return value + // which is passed to the final target, along with the unaffected arguments. + // collect_{N}_{T} collects N arguments at any position into a T value + // collect_{N}_S{S}_{T} collects N arguments at slot S into a T value + // collect_{T} collects any number of arguments at any position + // filter_S{S}_{T} is the same as collect_1_S{S}_{T} (a unary collection) + // (collect_2 is also usable as a filter, with long or double arguments) + _adapter_opt_collect_ref, // combine N arguments, replace with a reference + _adapter_opt_collect_int, // combine N arguments, replace with an int, short, etc. + _adapter_opt_collect_long, // combine N arguments, replace with a long + _adapter_opt_collect_float, // combine N arguments, replace with a float + _adapter_opt_collect_double, // combine N arguments, replace with a double + _adapter_opt_collect_void, // combine N arguments, replace with nothing + // if there is a small fixed number to push, do so without a loop: + _adapter_opt_collect_0_ref, // collect N=0 arguments, insert a reference + _adapter_opt_collect_1_ref, // collect N=1 argument, replace with a reference + _adapter_opt_collect_2_ref, // combine N=2 arguments, replace with a reference + _adapter_opt_collect_3_ref, // combine N=3 arguments, replace with a reference + _adapter_opt_collect_4_ref, // combine N=4 arguments, replace with a reference + _adapter_opt_collect_5_ref, // combine N=5 arguments, replace with a reference + // filters are an important special case because they never move arguments: + _adapter_opt_filter_S0_ref, // filter N=1 argument at S=0, replace with a reference + _adapter_opt_filter_S1_ref, // filter N=1 argument at S=1, replace with a reference + _adapter_opt_filter_S2_ref, // filter N=1 argument at S=2, replace with a reference + _adapter_opt_filter_S3_ref, // filter N=1 argument at S=3, replace with a reference + _adapter_opt_filter_S4_ref, // filter N=1 argument at S=4, replace with a reference + _adapter_opt_filter_S5_ref, // filter N=1 argument at S=5, replace with a reference + // these move arguments, but they are important for boxing + _adapter_opt_collect_2_S0_ref, // combine last N=2 arguments, replace with a reference + _adapter_opt_collect_2_S1_ref, // combine N=2 arguments at S=1, replace with a reference + _adapter_opt_collect_2_S2_ref, // combine N=2 arguments at S=2, replace with a reference + _adapter_opt_collect_2_S3_ref, // combine N=2 arguments at S=3, replace with a reference + _adapter_opt_collect_2_S4_ref, // combine N=2 arguments at S=4, replace with a reference + _adapter_opt_collect_2_S5_ref, // combine N=2 arguments at S=5, replace with a reference + _adapter_opt_collect_FIRST = _adapter_opt_collect_ref, + _adapter_opt_collect_LAST = _adapter_opt_collect_2_S5_ref, + + // blocking folding conversions + // these are like collects, but retain all the N arguments for the final target + //_adapter_opt_fold_0_ref, // same as _adapter_opt_collect_0_ref + // fold_{N}_{T} processes N arguments at any position into a T value, which it inserts + // fold_{T} processes any number of arguments at any position + _adapter_opt_fold_ref, // process N arguments, prepend a reference + _adapter_opt_fold_int, // process N arguments, prepend an int, short, etc. + _adapter_opt_fold_long, // process N arguments, prepend a long + _adapter_opt_fold_float, // process N arguments, prepend a float + _adapter_opt_fold_double, // process N arguments, prepend a double + _adapter_opt_fold_void, // process N arguments, but leave the list unchanged + _adapter_opt_fold_1_ref, // process N=1 argument, prepend a reference + _adapter_opt_fold_2_ref, // process N=2 arguments, prepend a reference + _adapter_opt_fold_3_ref, // process N=3 arguments, prepend a reference + _adapter_opt_fold_4_ref, // process N=4 arguments, prepend a reference + _adapter_opt_fold_5_ref, // process N=5 arguments, prepend a reference + _adapter_opt_fold_FIRST = _adapter_opt_fold_ref, + _adapter_opt_fold_LAST = _adapter_opt_fold_5_ref, _EK_LIMIT, _EK_FIRST = 0 @@ -110,6 +199,7 @@ class MethodHandles: AllStatic { enum { // import java_lang_invoke_AdapterMethodHandle::CONV_OP_* CONV_OP_LIMIT = java_lang_invoke_AdapterMethodHandle::CONV_OP_LIMIT, CONV_OP_MASK = java_lang_invoke_AdapterMethodHandle::CONV_OP_MASK, + CONV_TYPE_MASK = java_lang_invoke_AdapterMethodHandle::CONV_TYPE_MASK, CONV_VMINFO_MASK = java_lang_invoke_AdapterMethodHandle::CONV_VMINFO_MASK, CONV_VMINFO_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_VMINFO_SHIFT, CONV_OP_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_OP_SHIFT, @@ -123,6 +213,7 @@ class MethodHandles: AllStatic { static MethodHandleEntry* _entries[_EK_LIMIT]; static const char* _entry_names[_EK_LIMIT+1]; static jobject _raise_exception_method; + static address _adapter_return_handlers[CONV_TYPE_MASK+1]; // Adapters. static MethodHandlesAdapterBlob* _adapter_code; @@ -147,39 +238,195 @@ class MethodHandles: AllStatic { } // Some adapter helper functions. - static void get_ek_bound_mh_info(EntryKind ek, BasicType& arg_type, int& arg_mask, int& arg_slots) { + static EntryKind ek_original_kind(EntryKind ek) { + if (ek <= _adapter_mh_last) return ek; switch (ek) { - case _bound_int_mh : // fall-thru - case _bound_int_direct_mh : arg_type = T_INT; arg_mask = _INSERT_INT_MASK; break; - case _bound_long_mh : // fall-thru - case _bound_long_direct_mh: arg_type = T_LONG; arg_mask = _INSERT_LONG_MASK; break; - case _bound_ref_mh : // fall-thru - case _bound_ref_direct_mh : arg_type = T_OBJECT; arg_mask = _INSERT_REF_MASK; break; - default: ShouldNotReachHere(); + case _adapter_opt_swap_1: + case _adapter_opt_swap_2: + return _adapter_swap_args; + case _adapter_opt_rot_1_up: + case _adapter_opt_rot_1_down: + case _adapter_opt_rot_2_up: + case _adapter_opt_rot_2_down: + return _adapter_rot_args; + case _adapter_opt_i2i: + case _adapter_opt_l2i: + case _adapter_opt_d2f: + case _adapter_opt_i2l: + case _adapter_opt_f2d: + return _adapter_prim_to_prim; + case _adapter_opt_unboxi: + case _adapter_opt_unboxl: + return _adapter_ref_to_prim; } - arg_slots = type2size[arg_type]; + if (ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST) + return _adapter_spread_args; + if (ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST) + return _adapter_collect_args; + if (ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST) + return _adapter_fold_args; + if (ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST) + return _adapter_opt_return_any; + assert(false, "oob"); + return _EK_LIMIT; } - static void get_ek_adapter_opt_swap_rot_info(EntryKind ek, int& swap_bytes, int& rotate) { - int swap_slots = 0; + static bool ek_supported(MethodHandles::EntryKind ek); + + static BasicType ek_bound_mh_arg_type(EntryKind ek) { switch (ek) { - case _adapter_opt_swap_1: swap_slots = 1; rotate = 0; break; - case _adapter_opt_swap_2: swap_slots = 2; rotate = 0; break; - case _adapter_opt_rot_1_up: swap_slots = 1; rotate = 1; break; - case _adapter_opt_rot_1_down: swap_slots = 1; rotate = -1; break; - case _adapter_opt_rot_2_up: swap_slots = 2; rotate = 1; break; - case _adapter_opt_rot_2_down: swap_slots = 2; rotate = -1; break; - default: ShouldNotReachHere(); + case _bound_int_mh : // fall-thru + case _bound_int_direct_mh : return T_INT; + case _bound_long_mh : // fall-thru + case _bound_long_direct_mh : return T_LONG; + default : return T_OBJECT; } - // Return the size of the stack slots to move in bytes. - swap_bytes = swap_slots * Interpreter::stackElementSize; } - static int get_ek_adapter_opt_spread_info(EntryKind ek) { + static int ek_adapter_opt_swap_slots(EntryKind ek) { switch (ek) { - case _adapter_opt_spread_0: return 0; - case _adapter_opt_spread_1: return 1; - default : return -1; + case _adapter_opt_swap_1 : return 1; + case _adapter_opt_swap_2 : return 2; + case _adapter_opt_rot_1_up : return 1; + case _adapter_opt_rot_1_down : return 1; + case _adapter_opt_rot_2_up : return 2; + case _adapter_opt_rot_2_down : return 2; + default : ShouldNotReachHere(); return -1; + } + } + + static int ek_adapter_opt_swap_mode(EntryKind ek) { + switch (ek) { + case _adapter_opt_swap_1 : return 0; + case _adapter_opt_swap_2 : return 0; + case _adapter_opt_rot_1_up : return 1; + case _adapter_opt_rot_1_down : return -1; + case _adapter_opt_rot_2_up : return 1; + case _adapter_opt_rot_2_down : return -1; + default : ShouldNotReachHere(); return 0; + } + } + + static int ek_adapter_opt_collect_count(EntryKind ek) { + assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST || + ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, ""); + switch (ek) { + case _adapter_opt_collect_0_ref : return 0; + case _adapter_opt_filter_S0_ref : + case _adapter_opt_filter_S1_ref : + case _adapter_opt_filter_S2_ref : + case _adapter_opt_filter_S3_ref : + case _adapter_opt_filter_S4_ref : + case _adapter_opt_filter_S5_ref : + case _adapter_opt_fold_1_ref : + case _adapter_opt_collect_1_ref : return 1; + case _adapter_opt_collect_2_S0_ref : + case _adapter_opt_collect_2_S1_ref : + case _adapter_opt_collect_2_S2_ref : + case _adapter_opt_collect_2_S3_ref : + case _adapter_opt_collect_2_S4_ref : + case _adapter_opt_collect_2_S5_ref : + case _adapter_opt_fold_2_ref : + case _adapter_opt_collect_2_ref : return 2; + case _adapter_opt_fold_3_ref : + case _adapter_opt_collect_3_ref : return 3; + case _adapter_opt_fold_4_ref : + case _adapter_opt_collect_4_ref : return 4; + case _adapter_opt_fold_5_ref : + case _adapter_opt_collect_5_ref : return 5; + default : return -1; // sentinel value for "variable" + } + } + + static int ek_adapter_opt_collect_slot(EntryKind ek) { + assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST || + ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, ""); + switch (ek) { + case _adapter_opt_collect_2_S0_ref : + case _adapter_opt_filter_S0_ref : return 0; + case _adapter_opt_collect_2_S1_ref : + case _adapter_opt_filter_S1_ref : return 1; + case _adapter_opt_collect_2_S2_ref : + case _adapter_opt_filter_S2_ref : return 2; + case _adapter_opt_collect_2_S3_ref : + case _adapter_opt_filter_S3_ref : return 3; + case _adapter_opt_collect_2_S4_ref : + case _adapter_opt_filter_S4_ref : return 4; + case _adapter_opt_collect_2_S5_ref : + case _adapter_opt_filter_S5_ref : return 5; + default : return -1; // sentinel value for "variable" + } + } + + static BasicType ek_adapter_opt_collect_type(EntryKind ek) { + assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST || + ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, ""); + switch (ek) { + case _adapter_opt_fold_int : + case _adapter_opt_collect_int : return T_INT; + case _adapter_opt_fold_long : + case _adapter_opt_collect_long : return T_LONG; + case _adapter_opt_fold_float : + case _adapter_opt_collect_float : return T_FLOAT; + case _adapter_opt_fold_double : + case _adapter_opt_collect_double : return T_DOUBLE; + case _adapter_opt_fold_void : + case _adapter_opt_collect_void : return T_VOID; + default : return T_OBJECT; + } + } + + static int ek_adapter_opt_return_slot(EntryKind ek) { + assert(ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST, ""); + switch (ek) { + case _adapter_opt_return_S0_ref : return 0; + case _adapter_opt_return_S1_ref : return 1; + case _adapter_opt_return_S2_ref : return 2; + case _adapter_opt_return_S3_ref : return 3; + case _adapter_opt_return_S4_ref : return 4; + case _adapter_opt_return_S5_ref : return 5; + default : return -1; // sentinel value for "variable" + } + } + + static BasicType ek_adapter_opt_return_type(EntryKind ek) { + assert(ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST, ""); + switch (ek) { + case _adapter_opt_return_int : return T_INT; + case _adapter_opt_return_long : return T_LONG; + case _adapter_opt_return_float : return T_FLOAT; + case _adapter_opt_return_double : return T_DOUBLE; + case _adapter_opt_return_void : return T_VOID; + case _adapter_opt_return_any : return T_CONFLICT; // sentinel value for "variable" + default : return T_OBJECT; + } + } + + static int ek_adapter_opt_spread_count(EntryKind ek) { + assert(ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST, ""); + switch (ek) { + case _adapter_opt_spread_0 : return 0; + case _adapter_opt_spread_1_ref : return 1; + case _adapter_opt_spread_2_ref : return 2; + case _adapter_opt_spread_3_ref : return 3; + case _adapter_opt_spread_4_ref : return 4; + case _adapter_opt_spread_5_ref : return 5; + default : return -1; // sentinel value for "variable" + } + } + + static BasicType ek_adapter_opt_spread_type(EntryKind ek) { + assert(ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST, ""); + switch (ek) { + // (there is no _adapter_opt_spread_boolean; we use byte) + case _adapter_opt_spread_byte : return T_BYTE; + case _adapter_opt_spread_char : return T_CHAR; + case _adapter_opt_spread_short : return T_SHORT; + case _adapter_opt_spread_int : return T_INT; + case _adapter_opt_spread_long : return T_LONG; + case _adapter_opt_spread_float : return T_FLOAT; + case _adapter_opt_spread_double : return T_DOUBLE; + default : return T_OBJECT; } } @@ -228,12 +475,21 @@ class MethodHandles: AllStatic { // Bit mask of conversion_op values. May vary by platform. static int adapter_conversion_ops_supported_mask(); + static bool conv_op_supported(int conv_op) { + assert(conv_op_valid(conv_op), ""); + return ((adapter_conversion_ops_supported_mask() & nth_bit(conv_op)) != 0); + } + // Offset in words that the interpreter stack pointer moves when an argument is pushed. // The stack_move value must always be a multiple of this. static int stack_move_unit() { return frame::interpreter_frame_expression_stack_direction() * Interpreter::stackElementWords; } + // Adapter frame traversal. (Implementation-specific.) + static frame ricochet_frame_sender(const frame& fr, RegisterMap* reg_map); + static void ricochet_frame_oops_do(const frame& fr, OopClosure* blk, const RegisterMap* reg_map); + enum { CONV_VMINFO_SIGN_FLAG = 0x80 }; // Shift values for prim-to-prim conversions. static int adapter_prim_to_prim_subword_vminfo(BasicType dest) { @@ -429,6 +685,7 @@ class MethodHandles: AllStatic { // Fill in the fields of an AdapterMethodHandle mh. (MH.type must be pre-filled.) static void init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS); + static void ensure_vmlayout_field(Handle target, TRAPS); #ifdef ASSERT static bool spot_check_entry_names(); @@ -448,12 +705,54 @@ class MethodHandles: AllStatic { return same_basic_type_for_arguments(src, dst, raw, true); } - enum { // arg_mask values + static Symbol* convert_to_signature(oop type_str, bool polymorphic, TRAPS); + +#ifdef TARGET_ARCH_x86 +# include "methodHandles_x86.hpp" +#endif +#ifdef TARGET_ARCH_sparc +#define TARGET_ARCH_NYI_6939861 1 //FIXME +//# include "methodHandles_sparc.hpp" +#endif +#ifdef TARGET_ARCH_zero +#define TARGET_ARCH_NYI_6939861 1 //FIXME +//# include "methodHandles_zero.hpp" +#endif +#ifdef TARGET_ARCH_arm +#define TARGET_ARCH_NYI_6939861 1 //FIXME +//# include "methodHandles_arm.hpp" +#endif +#ifdef TARGET_ARCH_ppc +#define TARGET_ARCH_NYI_6939861 1 //FIXME +//# include "methodHandles_ppc.hpp" +#endif + +#ifdef TARGET_ARCH_NYI_6939861 + // Here are some backward compatible declarations until the 6939861 ports are updated. + #define _adapter_flyby (_EK_LIMIT + 10) + #define _adapter_ricochet (_EK_LIMIT + 11) + #define _adapter_opt_spread_1 _adapter_opt_spread_1_ref + #define _adapter_opt_spread_more _adapter_opt_spread_ref + enum { _INSERT_NO_MASK = -1, _INSERT_REF_MASK = 0, _INSERT_INT_MASK = 1, _INSERT_LONG_MASK = 3 }; + static void get_ek_bound_mh_info(EntryKind ek, BasicType& arg_type, int& arg_mask, int& arg_slots) { + arg_type = ek_bound_mh_arg_type(ek); + arg_mask = 0; + arg_slots = type2size[arg_type];; + } + static void get_ek_adapter_opt_swap_rot_info(EntryKind ek, int& swap_bytes, int& rotate) { + int swap_slots = ek_adapter_opt_swap_slots(ek); + rotate = ek_adapter_opt_swap_mode(ek); + swap_bytes = swap_slots * Interpreter::stackElementSize; + } + static int get_ek_adapter_opt_spread_info(EntryKind ek) { + return ek_adapter_opt_spread_count(ek); + } + static void insert_arg_slots(MacroAssembler* _masm, RegisterOrConstant arg_slots, int arg_mask, @@ -466,8 +765,7 @@ class MethodHandles: AllStatic { Register temp_reg, Register temp2_reg, Register temp3_reg = noreg); static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN; - - static Symbol* convert_to_signature(oop type_str, bool polymorphic, TRAPS); +#endif //TARGET_ARCH_NYI_6939861 }; diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp index b5352fc00d6..efa97157f34 100644 --- a/hotspot/src/share/vm/runtime/frame.cpp +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -33,6 +33,7 @@ #include "oops/methodOop.hpp" #include "oops/oop.inline.hpp" #include "oops/oop.inline2.hpp" +#include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaCalls.hpp" @@ -169,6 +170,11 @@ void frame::set_pc(address newpc ) { } // type testers +bool frame::is_ricochet_frame() const { + RicochetBlob* rcb = SharedRuntime::ricochet_blob(); + return (_cb == rcb && rcb != NULL && rcb->returns_to_bounce_addr(_pc)); +} + bool frame::is_deoptimized_frame() const { assert(_deopt_state != unknown, "not answerable"); return _deopt_state == is_deoptimized; @@ -341,12 +347,18 @@ frame frame::java_sender() const { frame frame::real_sender(RegisterMap* map) const { frame result = sender(map); - while (result.is_runtime_frame()) { + while (result.is_runtime_frame() || + result.is_ricochet_frame()) { result = result.sender(map); } return result; } +frame frame::sender_for_ricochet_frame(RegisterMap* map) const { + assert(is_ricochet_frame(), ""); + return MethodHandles::ricochet_frame_sender(*this, map); +} + // Note: called by profiler - NOT for current thread frame frame::profile_find_Java_sender_frame(JavaThread *thread) { // If we don't recognize this frame, walk back up the stack until we do @@ -529,6 +541,7 @@ jint frame::interpreter_frame_expression_stack_size() const { const char* frame::print_name() const { if (is_native_frame()) return "Native"; if (is_interpreted_frame()) return "Interpreted"; + if (is_ricochet_frame()) return "Ricochet"; if (is_compiled_frame()) { if (is_deoptimized_frame()) return "Deoptimized"; return "Compiled"; @@ -715,6 +728,8 @@ void frame::print_on_error(outputStream* st, char* buf, int buflen, bool verbose st->print("v ~RuntimeStub::%s", ((RuntimeStub *)_cb)->name()); } else if (_cb->is_deoptimization_stub()) { st->print("v ~DeoptimizationBlob"); + } else if (_cb->is_ricochet_stub()) { + st->print("v ~RichochetBlob"); } else if (_cb->is_exception_stub()) { st->print("v ~ExceptionBlob"); } else if (_cb->is_safepoint_stub()) { @@ -978,6 +993,9 @@ void frame::oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, void frame::oops_code_blob_do(OopClosure* f, CodeBlobClosure* cf, const RegisterMap* reg_map) { assert(_cb != NULL, "sanity check"); + if (_cb == SharedRuntime::ricochet_blob()) { + oops_ricochet_do(f, reg_map); + } if (_cb->oop_maps() != NULL) { OopMapSet::oops_do(this, reg_map, f); @@ -996,6 +1014,11 @@ void frame::oops_code_blob_do(OopClosure* f, CodeBlobClosure* cf, const Register cf->do_code_blob(_cb); } +void frame::oops_ricochet_do(OopClosure* f, const RegisterMap* map) { + assert(is_ricochet_frame(), ""); + MethodHandles::ricochet_frame_oops_do(*this, f, map); +} + class CompiledArgumentOopFinder: public SignatureInfo { protected: OopClosure* _f; diff --git a/hotspot/src/share/vm/runtime/frame.hpp b/hotspot/src/share/vm/runtime/frame.hpp index 66d17a9f76e..7e750d5f0bc 100644 --- a/hotspot/src/share/vm/runtime/frame.hpp +++ b/hotspot/src/share/vm/runtime/frame.hpp @@ -135,6 +135,7 @@ class frame VALUE_OBJ_CLASS_SPEC { bool is_interpreted_frame() const; bool is_java_frame() const; bool is_entry_frame() const; // Java frame called from C? + bool is_ricochet_frame() const; bool is_native_frame() const; bool is_runtime_frame() const; bool is_compiled_frame() const; @@ -175,6 +176,7 @@ class frame VALUE_OBJ_CLASS_SPEC { // Helper methods for better factored code in frame::sender frame sender_for_compiled_frame(RegisterMap* map) const; frame sender_for_entry_frame(RegisterMap* map) const; + frame sender_for_ricochet_frame(RegisterMap* map) const; frame sender_for_interpreter_frame(RegisterMap* map) const; frame sender_for_native_frame(RegisterMap* map) const; @@ -400,6 +402,7 @@ class frame VALUE_OBJ_CLASS_SPEC { // Oops-do's void oops_compiled_arguments_do(Symbol* signature, bool has_receiver, const RegisterMap* reg_map, OopClosure* f); void oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache = true); + void oops_ricochet_do(OopClosure* f, const RegisterMap* map); private: void oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f); diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index cdfa352328d..9bfc49c4964 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -3708,6 +3708,10 @@ class CommandLineFlags { diagnostic(bool, OptimizeMethodHandles, true, \ "when constructing method handles, try to improve them") \ \ + diagnostic(bool, UseRicochetFrames, true, \ + "use ricochet stack frames for method handle combination, " \ + "if the platform supports them") \ + \ experimental(bool, TrustFinalNonStaticFields, false, \ "trust final non-static declarations for constant folding") \ \ diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.cpp b/hotspot/src/share/vm/runtime/sharedRuntime.cpp index f60b2f07029..d13b734caf6 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp +++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp @@ -88,6 +88,8 @@ HS_DTRACE_PROBE_DECL7(hotspot, method__entry, int, HS_DTRACE_PROBE_DECL7(hotspot, method__return, int, char*, int, char*, int, char*, int); +RicochetBlob* SharedRuntime::_ricochet_blob = NULL; + // Implementation of SharedRuntime #ifndef PRODUCT @@ -460,6 +462,10 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* thre if (Interpreter::contains(return_address)) { return Interpreter::rethrow_exception_entry(); } + // Ricochet frame unwind code + if (SharedRuntime::ricochet_blob() != NULL && SharedRuntime::ricochet_blob()->returns_to_bounce_addr(return_address)) { + return SharedRuntime::ricochet_blob()->exception_addr(); + } guarantee(blob == NULL || !blob->is_runtime_stub(), "caller should have skipped stub"); guarantee(!VtableStubs::contains(return_address), "NULL exceptions in vtables should have been handled already!"); @@ -1174,6 +1180,7 @@ JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_ic_miss(JavaThread* assert(stub_frame.is_runtime_frame(), "sanity check"); frame caller_frame = stub_frame.sender(®_map); assert(!caller_frame.is_interpreted_frame() && !caller_frame.is_entry_frame(), "unexpected frame"); + assert(!caller_frame.is_ricochet_frame(), "unexpected frame"); #endif /* ASSERT */ methodHandle callee_method; @@ -1222,6 +1229,7 @@ JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method(JavaThread* thread)) if (caller_frame.is_interpreted_frame() || caller_frame.is_entry_frame() || + caller_frame.is_ricochet_frame() || is_mh_invoke_via_adapter) { methodOop callee = thread->callee_target(); guarantee(callee != NULL && callee->is_method(), "bad handshake"); diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.hpp b/hotspot/src/share/vm/runtime/sharedRuntime.hpp index 268bd3a2f4e..f1c5c7f08c9 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntime.hpp +++ b/hotspot/src/share/vm/runtime/sharedRuntime.hpp @@ -58,6 +58,8 @@ class SharedRuntime: AllStatic { static RuntimeStub* _resolve_virtual_call_blob; static RuntimeStub* _resolve_static_call_blob; + static RicochetBlob* _ricochet_blob; + static SafepointBlob* _polling_page_safepoint_handler_blob; static SafepointBlob* _polling_page_return_handler_blob; #ifdef COMPILER2 @@ -213,6 +215,16 @@ class SharedRuntime: AllStatic { return _resolve_static_call_blob->entry_point(); } + static RicochetBlob* ricochet_blob() { +#ifdef X86 + // Currently only implemented on x86 + assert(!EnableInvokeDynamic || _ricochet_blob != NULL, "oops"); +#endif + return _ricochet_blob; + } + + static void generate_ricochet_blob(); + static SafepointBlob* polling_page_return_handler_blob() { return _polling_page_return_handler_blob; } static SafepointBlob* polling_page_safepoint_handler_blob() { return _polling_page_safepoint_handler_blob; } diff --git a/hotspot/src/share/vm/services/heapDumper.cpp b/hotspot/src/share/vm/services/heapDumper.cpp index 0252ddfff7c..46df8b721da 100644 --- a/hotspot/src/share/vm/services/heapDumper.cpp +++ b/hotspot/src/share/vm/services/heapDumper.cpp @@ -1649,6 +1649,9 @@ int VM_HeapDumper::do_thread(JavaThread* java_thread, u4 thread_serial_num) { if (fr->is_entry_frame()) { last_entry_frame = fr; } + if (fr->is_ricochet_frame()) { + fr->oops_ricochet_do(&blk, vf->register_map()); + } } vf = vf->sender(); } From d814ae14d1081df314eb5da12774659721603a84 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Fri, 6 May 2011 17:06:25 -0700 Subject: [PATCH 07/65] 7011326: Add informative example to @SafeVarargs type or language discussion Reviewed-by: mcimadamore, mduigou --- .../share/classes/java/lang/SafeVarargs.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/jdk/src/share/classes/java/lang/SafeVarargs.java b/jdk/src/share/classes/java/lang/SafeVarargs.java index b0c03a9162b..818ea21bafb 100644 --- a/jdk/src/share/classes/java/lang/SafeVarargs.java +++ b/jdk/src/share/classes/java/lang/SafeVarargs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2011, 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,7 +32,7 @@ import java.lang.annotation.*; * constructor does not perform potentially unsafe operations on its * varargs parameter. Applying this annotation to a method or * constructor suppresses unchecked warnings about a - * non-reifiable variable-arity (vararg) type and suppresses + * non-reifiable variable arity (vararg) type and suppresses * unchecked warnings about parameterized array creation at call * sites. * @@ -41,11 +41,10 @@ import java.lang.annotation.*; * additional usage restrictions on this annotation type; it is a * compile-time error if a method or constructor declaration is * annotated with a {@code @SafeVarargs} annotation, and either: - *
    - *
  • the declaration is a fixed-arity method or constructor + *
  • the declaration is a fixed arity method or constructor * - *
  • the declaration is a variable-arity method that is neither + *
  • the declaration is a variable arity method that is neither * {@code static} nor {@code final}. * *
@@ -55,15 +54,28 @@ import java.lang.annotation.*; * *
    * - *
  • The variable-arity parameter has a reifiable element type, + *
  • The variable arity parameter has a reifiable element type, * which includes primitive types, {@code Object}, and {@code String}. * (The unchecked warnings this annotation type suppresses already do * not occur for a reifiable element type.) * *
  • The body of the method or constructor declaration performs * potentially unsafe operations, such as an assignment to an element - * of the variable-arity parameter's array that generates an unchecked - * warning. + * of the variable arity parameter's array that generates an unchecked + * warning. Some unsafe operations do not trigger an unchecked + * warning. For example, the aliasing in + * + *
    + * @SafeVarargs // Not actually safe!
    + * static void m(List<String>... stringLists) {
    + *   Object[] array = stringLists;
    + *   List<Integer> tmpList = Arrays.asList(42);
    + *   array[0] = tmpList; // Semantically invalid, but compiles without warnings
    + *   String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
    + * }
    + * 
    + * + * leads to a {@code ClassCastException} at runtime. * *

    Future versions of the platform may mandate compiler errors for * such unsafe operations. From 691c55d04af0e212a95a2cab9f72a008b0770d3b Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Mon, 9 May 2011 15:58:25 +0100 Subject: [PATCH 08/65] 6987652: VM crashed in sun.security.mscapi.RSAKeyPairGenerator.generateRSAKeyPair(...) Reviewed-by: alanb --- jdk/src/windows/native/sun/security/mscapi/security.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/windows/native/sun/security/mscapi/security.cpp b/jdk/src/windows/native/sun/security/mscapi/security.cpp index 1a489d70eca..49cbe81e418 100644 --- a/jdk/src/windows/native/sun/security/mscapi/security.cpp +++ b/jdk/src/windows/native/sun/security/mscapi/security.cpp @@ -705,7 +705,7 @@ JNIEXPORT jobject JNICALL Java_sun_security_mscapi_RSAKeyPairGenerator_generateR HCRYPTPROV hCryptProv = NULL; HCRYPTKEY hKeyPair; DWORD dwFlags = (keySize << 16) | CRYPT_EXPORTABLE; - jobject keypair; + jobject keypair = NULL; const char* pszKeyContainerName = NULL; // UUID __try From 25ec11dfe6637bb3a0b77029694191e6127c2c29 Mon Sep 17 00:00:00 2001 From: Doug Lea Date: Mon, 9 May 2011 16:36:20 +0100 Subject: [PATCH 09/65] 7042673: LockSupport.getBlocker(null) crashes Reviewed-by: chegar --- .../share/classes/java/util/concurrent/locks/LockSupport.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java b/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java index f0cd3bc0456..7b829f63a06 100644 --- a/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java +++ b/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java @@ -275,10 +275,14 @@ public class LockSupport { * snapshot -- the thread may have since unblocked or blocked on a * different blocker object. * + * @param t the thread * @return the blocker + * @throws NullPointerException if argument is null * @since 1.6 */ public static Object getBlocker(Thread t) { + if (t == null) + throw new NullPointerException(); return unsafe.getObjectVolatile(t, parkBlockerOffset); } From 8b1efebaa18a5ccee8b89a2b9cf25f1f0f231af4 Mon Sep 17 00:00:00 2001 From: Mike Duigou Date: Mon, 9 May 2011 09:13:47 -0700 Subject: [PATCH 10/65] 7043104: disable test java/lang/invoke/InvokeDynamicPrintArgs.java Reviewed-by: alanb --- jdk/test/ProblemList.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 07bd29aecbf..3d883e8f7d8 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -195,6 +195,9 @@ java/beans/XMLEncoder/6329581/Test6329581.java generic-all # jdk_lang +# requires junit +java/lang/invoke/InvokeDynamicPrintArgs.java generic-all + # Times out on solaris 10 sparc java/lang/ClassLoader/Assert.java generic-all From 914105d32e9760719a039f165bbf306afbee5a1e Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Mon, 9 May 2011 18:45:33 +0100 Subject: [PATCH 11/65] 7042979: Rename StandardSocketOption and StandardWatchEventKind Reviewed-by: forax, chegar --- jdk/make/com/sun/nio/sctp/FILES_java.gmk | 2 +- jdk/make/java/nio/FILES_java.gmk | 2 +- .../classes/com/sun/nio/sctp/MessageInfo.java | 4 +-- .../classes/com/sun/nio/sctp/SctpChannel.java | 30 ++++++++--------- .../com/sun/nio/sctp/SctpMultiChannel.java | 30 ++++++++--------- .../com/sun/nio/sctp/SctpServerChannel.java | 6 ++-- .../com/sun/nio/sctp/SctpSocketOption.java | 2 +- ...on.java => SctpStandardSocketOptions.java} | 14 ++++---- .../share/classes/java/net/SocketOption.java | 2 +- ...Option.java => StandardSocketOptions.java} | 4 +-- .../AsynchronousServerSocketChannel.java | 4 +-- .../channels/AsynchronousSocketChannel.java | 10 +++--- .../java/nio/channels/DatagramChannel.java | 16 +++++----- .../java/nio/channels/MulticastChannel.java | 8 ++--- .../java/nio/channels/NetworkChannel.java | 4 +-- .../nio/channels/ServerSocketChannel.java | 4 +-- .../java/nio/channels/SocketChannel.java | 12 +++---- jdk/src/share/classes/java/nio/file/Path.java | 8 ++--- ...Kind.java => StandardWatchEventKinds.java} | 8 ++--- .../classes/java/nio/file/WatchEvent.java | 8 ++--- .../classes/java/nio/file/WatchService.java | 4 +-- .../classes/java/nio/file/Watchable.java | 2 +- .../AsynchronousServerSocketChannelImpl.java | 6 ++-- .../nio/ch/AsynchronousSocketChannelImpl.java | 12 +++---- .../sun/nio/ch/DatagramChannelImpl.java | 32 +++++++++---------- .../sun/nio/ch/DatagramSocketAdaptor.java | 20 ++++++------ .../sun/nio/ch/ExtendedSocketOption.java | 2 +- jdk/src/share/classes/sun/nio/ch/Net.java | 10 +++--- .../sun/nio/ch/ServerSocketAdaptor.java | 8 ++--- .../sun/nio/ch/ServerSocketChannelImpl.java | 4 +-- .../classes/sun/nio/ch/SocketAdaptor.java | 28 ++++++++-------- .../classes/sun/nio/ch/SocketChannelImpl.java | 18 +++++------ .../classes/sun/nio/fs/AbstractPoller.java | 8 ++--- .../classes/sun/nio/fs/AbstractWatchKey.java | 14 ++++---- .../sun/nio/fs/PollingWatchService.java | 24 +++++++------- .../sun/nio/ch/genSocketOptionRegistry.c | 32 +++++++++---------- .../sample/nio/chatserver/ChatServer.java | 6 ++-- jdk/src/share/sample/nio/file/WatchDir.java | 2 +- .../share/sample/nio/multicast/Reader.java | 2 +- .../share/sample/nio/multicast/Sender.java | 2 +- .../classes/sun/nio/ch/SctpChannelImpl.java | 2 +- .../sun/nio/ch/SctpMultiChannelImpl.java | 2 +- .../solaris/classes/sun/nio/ch/SctpNet.java | 2 +- .../sun/nio/ch/SctpServerChannelImpl.java | 4 +-- .../classes/sun/nio/fs/LinuxWatchService.java | 20 ++++++------ .../sun/nio/fs/SolarisWatchService.java | 18 +++++------ .../sun/nio/fs/WindowsWatchService.java | 10 +++--- .../sctp/SctpChannel/SocketOptionTests.java | 2 +- .../SctpMultiChannel/SocketOptionTests.java | 2 +- .../Basic.java | 2 +- .../AsynchronousSocketChannel/Basic.java | 4 +-- .../DatagramChannel/BasicMulticastTests.java | 4 +-- .../MulticastSendReceiveTests.java | 4 +-- .../DatagramChannel/SocketOptionTests.java | 2 +- .../SocketOptionTests.java | 2 +- .../nio/channels/SocketChannel/Shutdown.java | 2 +- .../SocketChannel/SocketOptionTests.java | 2 +- .../java/nio/file/Files/CheckPermissions.java | 2 +- .../java/nio/file/WatchService/Basic.java | 14 ++++---- .../file/WatchService/FileTreeModifier.java | 2 +- .../nio/file/WatchService/LotsOfEvents.java | 2 +- .../WatchService/SensitivityModifier.java | 2 +- .../WatchService/WithSecurityManager.java | 2 +- 63 files changed, 261 insertions(+), 261 deletions(-) rename jdk/src/share/classes/com/sun/nio/sctp/{SctpStandardSocketOption.java => SctpStandardSocketOptions.java} (97%) rename jdk/src/share/classes/java/net/{StandardSocketOption.java => StandardSocketOptions.java} (99%) rename jdk/src/share/classes/java/nio/file/{StandardWatchEventKind.java => StandardWatchEventKinds.java} (94%) diff --git a/jdk/make/com/sun/nio/sctp/FILES_java.gmk b/jdk/make/com/sun/nio/sctp/FILES_java.gmk index 9d9c1a98e91..725131e7280 100644 --- a/jdk/make/com/sun/nio/sctp/FILES_java.gmk +++ b/jdk/make/com/sun/nio/sctp/FILES_java.gmk @@ -38,7 +38,7 @@ FILES_java = \ com/sun/nio/sctp/SctpMultiChannel.java \ com/sun/nio/sctp/SctpServerChannel.java \ com/sun/nio/sctp/SctpSocketOption.java \ - com/sun/nio/sctp/SctpStandardSocketOption.java \ + com/sun/nio/sctp/SctpStandardSocketOptions.java \ com/sun/nio/sctp/SendFailedNotification.java \ com/sun/nio/sctp/ShutdownNotification.java \ \ diff --git a/jdk/make/java/nio/FILES_java.gmk b/jdk/make/java/nio/FILES_java.gmk index f50f2fd1efb..41397cacdc0 100644 --- a/jdk/make/java/nio/FILES_java.gmk +++ b/jdk/make/java/nio/FILES_java.gmk @@ -116,7 +116,7 @@ FILES_src = \ java/nio/file/SimpleFileVisitor.java \ java/nio/file/StandardCopyOption.java \ java/nio/file/StandardOpenOption.java \ - java/nio/file/StandardWatchEventKind.java \ + java/nio/file/StandardWatchEventKinds.java \ java/nio/file/TempFileHelper.java \ java/nio/file/WatchEvent.java \ java/nio/file/WatchKey.java \ diff --git a/jdk/src/share/classes/com/sun/nio/sctp/MessageInfo.java b/jdk/src/share/classes/com/sun/nio/sctp/MessageInfo.java index ca3e13fc47a..b851b77ac43 100644 --- a/jdk/src/share/classes/com/sun/nio/sctp/MessageInfo.java +++ b/jdk/src/share/classes/com/sun/nio/sctp/MessageInfo.java @@ -179,7 +179,7 @@ public abstract class MessageInfo { * completely received. For messages being sent {@code true} indicates that * the message is complete, {@code false} indicates that the message is not * complete. How the send channel interprets this value depends on the value - * of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE + * of its {@link SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE * SCTP_EXPLICIT_COMPLETE} socket option. * * @return {@code true} if, and only if, the message is complete @@ -192,7 +192,7 @@ public abstract class MessageInfo { *

    For messages being sent {@code true} indicates that * the message is complete, {@code false} indicates that the message is not * complete. How the send channel interprets this value depends on the value - * of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE + * of its {@link SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE * SCTP_EXPLICIT_COMPLETE} socket option. * * @param complete diff --git a/jdk/src/share/classes/com/sun/nio/sctp/SctpChannel.java b/jdk/src/share/classes/com/sun/nio/sctp/SctpChannel.java index 8e0472ef1fa..d040a7a07ea 100644 --- a/jdk/src/share/classes/com/sun/nio/sctp/SctpChannel.java +++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpChannel.java @@ -65,55 +65,55 @@ import java.nio.channels.SelectionKey; * Description * * - * {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS + * {@link SctpStandardSocketOptions#SCTP_DISABLE_FRAGMENTS * SCTP_DISABLE_FRAGMENTS} * Enables or disables message fragmentation * * - * {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE + * {@link SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE * SCTP_EXPLICIT_COMPLETE} * Enables or disables explicit message completion * * - * {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE + * {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE * SCTP_FRAGMENT_INTERLEAVE} * Controls how the presentation of messages occur for the message * receiver * * - * {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS + * {@link SctpStandardSocketOptions#SCTP_INIT_MAXSTREAMS * SCTP_INIT_MAXSTREAMS} * The maximum number of streams requested by the local endpoint during * association initialization * * - * {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} + * {@link SctpStandardSocketOptions#SCTP_NODELAY SCTP_NODELAY} * Enables or disable a Nagle-like algorithm * * - * {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR + * {@link SctpStandardSocketOptions#SCTP_PRIMARY_ADDR * SCTP_PRIMARY_ADDR} * Requests that the local SCTP stack use the given peer address as the * association primary * * - * {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR + * {@link SctpStandardSocketOptions#SCTP_SET_PEER_PRIMARY_ADDR * SCTP_SET_PEER_PRIMARY_ADDR} * Requests that the peer mark the enclosed address as the association * primary * * - * {@link SctpStandardSocketOption#SO_SNDBUF + * {@link SctpStandardSocketOptions#SO_SNDBUF * SO_SNDBUF} * The size of the socket send buffer * * - * {@link SctpStandardSocketOption#SO_RCVBUF + * {@link SctpStandardSocketOptions#SO_RCVBUF * SO_RCVBUF} * The size of the socket receive buffer * * - * {@link SctpStandardSocketOption#SO_LINGER + * {@link SctpStandardSocketOptions#SO_LINGER * SO_LINGER} * Linger on close if data is present (when configured in blocking mode * only) @@ -449,7 +449,7 @@ public abstract class SctpChannel *

    This is a convience method and is equivalent to evaluating the * following expression: *

    -     * setOption(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
    +     * setOption(SctpStandardSocketOptions.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
          *  .connect(remote);
          * 
    * @@ -651,7 +651,7 @@ public abstract class SctpChannel * @throws IOException * If an I/O error occurs * - * @see SctpStandardSocketOption + * @see SctpStandardSocketOptions */ public abstract T getOption(SctpSocketOption name) throws IOException; @@ -680,7 +680,7 @@ public abstract class SctpChannel * @throws IOException * If an I/O error occurs * - * @see SctpStandardSocketOption + * @see SctpStandardSocketOptions */ public abstract SctpChannel setOption(SctpSocketOption name, T value) throws IOException; @@ -731,7 +731,7 @@ public abstract class SctpChannel * MessageInfo} will return {@code false}, and more invocations of this * method will be necessary to completely consume the messgae. Only * one message at a time will be partially delivered in any stream. The - * socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE + * socket option {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of * messages occurs. * @@ -804,7 +804,7 @@ public abstract class SctpChannel * and sufficient room becomes available, then the remaining bytes in the * given byte buffer are transmitted as a single message. Sending a message * is atomic unless explicit message completion {@link - * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE} + * SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE} * socket option is enabled on this channel's socket. * *

    The message is transferred from the byte buffer as if by a regular diff --git a/jdk/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java b/jdk/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java index b2f0378064f..229d18ec039 100644 --- a/jdk/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java +++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java @@ -69,55 +69,55 @@ import java.nio.channels.SelectionKey; * Description * * - * {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS + * {@link SctpStandardSocketOptions#SCTP_DISABLE_FRAGMENTS * SCTP_DISABLE_FRAGMENTS} * Enables or disables message fragmentation * * - * {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE + * {@link SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE * SCTP_EXPLICIT_COMPLETE} * Enables or disables explicit message completion * * - * {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE + * {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE * SCTP_FRAGMENT_INTERLEAVE} * Controls how the presentation of messages occur for the message * receiver * * - * {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS + * {@link SctpStandardSocketOptions#SCTP_INIT_MAXSTREAMS * SCTP_INIT_MAXSTREAMS} * The maximum number of streams requested by the local endpoint during * association initialization * * - * {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} + * {@link SctpStandardSocketOptions#SCTP_NODELAY SCTP_NODELAY} * Enables or disable a Nagle-like algorithm * * - * {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR + * {@link SctpStandardSocketOptions#SCTP_PRIMARY_ADDR * SCTP_PRIMARY_ADDR} * Requests that the local SCTP stack use the given peer address as the * association primary * * - * {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR + * {@link SctpStandardSocketOptions#SCTP_SET_PEER_PRIMARY_ADDR * SCTP_SET_PEER_PRIMARY_ADDR} * Requests that the peer mark the enclosed address as the association * primary * * - * {@link SctpStandardSocketOption#SO_SNDBUF + * {@link SctpStandardSocketOptions#SO_SNDBUF * SO_SNDBUF} * The size of the socket send buffer * * - * {@link SctpStandardSocketOption#SO_RCVBUF + * {@link SctpStandardSocketOptions#SO_RCVBUF * SO_RCVBUF} * The size of the socket receive buffer * * - * {@link SctpStandardSocketOption#SO_LINGER + * {@link SctpStandardSocketOptions#SO_LINGER * SO_LINGER} * Linger on close if data is present (when configured in blocking mode * only) @@ -450,7 +450,7 @@ public abstract class SctpMultiChannel * @throws IOException * If an I/O error occurs * - * @see SctpStandardSocketOption + * @see SctpStandardSocketOptions */ public abstract T getOption(SctpSocketOption name, Association association) @@ -489,7 +489,7 @@ public abstract class SctpMultiChannel * @throws IOException * If an I/O error occurs * - * @see SctpStandardSocketOption + * @see SctpStandardSocketOptions */ public abstract SctpMultiChannel setOption(SctpSocketOption name, T value, @@ -542,7 +542,7 @@ public abstract class SctpMultiChannel * MessageInfo} will return {@code false}, and more invocations of this * method will be necessary to completely consume the messgae. Only * one message at a time will be partially delivered in any stream. The - * socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE + * socket option {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of * messages occurs. * @@ -635,14 +635,14 @@ public abstract class SctpMultiChannel * underlying output buffer, then the remaining bytes in the given byte * buffer are transmitted as a single message. Sending a message * is atomic unless explicit message completion {@link - * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE} + * SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE} * socket option is enabled on this channel's socket. * *

    If this channel is in non-blocking mode, there is sufficient room * in the underlying output buffer, and an implicit association setup is * required, then the remaining bytes in the given byte buffer are * transmitted as a single message, subject to {@link - * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}. + * SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}. * If for any reason the message cannot * be delivered an {@link AssociationChangeNotification association * changed} notification is put on the SCTP stack with its {@code event} parameter set diff --git a/jdk/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java b/jdk/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java index 5c762edfe35..3867fc9ca34 100644 --- a/jdk/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java +++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java @@ -53,7 +53,7 @@ import java.nio.channels.spi.AbstractSelectableChannel; * Description * * - * {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS + * {@link SctpStandardSocketOptions#SCTP_INIT_MAXSTREAMS * SCTP_INIT_MAXSTREAMS} * The maximum number of streams requested by the local endpoint during * association initialization @@ -360,7 +360,7 @@ public abstract class SctpServerChannel * @throws IOException * If an I/O error occurs * - * @see SctpStandardSocketOption + * @see SctpStandardSocketOptions */ public abstract T getOption(SctpSocketOption name) throws IOException; @@ -388,7 +388,7 @@ public abstract class SctpServerChannel * @throws IOException * If an I/O error occurs * - * @see SctpStandardSocketOption + * @see SctpStandardSocketOptions */ public abstract SctpServerChannel setOption(SctpSocketOption name, T value) diff --git a/jdk/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java b/jdk/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java index 37f954d7120..3e60dbfa090 100644 --- a/jdk/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java +++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java @@ -33,6 +33,6 @@ import java.net.SocketOption; * * @since 1.7 * - * @see SctpStandardSocketOption + * @see SctpStandardSocketOptions */ public interface SctpSocketOption extends SocketOption { } diff --git a/jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java b/jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOptions.java similarity index 97% rename from jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java rename to jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOptions.java index 47ffa98238a..22c5f4a005b 100644 --- a/jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java +++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOptions.java @@ -34,8 +34,8 @@ import sun.nio.ch.SctpStdSocketOption; * * @since 1.7 */ -public class SctpStandardSocketOption { - private SctpStandardSocketOption() {} +public class SctpStandardSocketOptions { + private SctpStandardSocketOptions() {} /** * Enables or disables message fragmentation. * @@ -127,7 +127,7 @@ public class SctpStandardSocketOption { * association initialization. * *

    The value of this socket option is an {@link - * SctpStandardSocketOption.InitMaxStreams InitMaxStreams}, that represents + * SctpStandardSocketOptions.InitMaxStreams InitMaxStreams}, that represents * the maximum number of inbound and outbound streams that an association * on the channel is prepared to support. * @@ -157,9 +157,9 @@ public class SctpStandardSocketOption { * the endpoints default value. */ public static final SctpSocketOption - SCTP_INIT_MAXSTREAMS = - new SctpStdSocketOption( - "SCTP_INIT_MAXSTREAMS", SctpStandardSocketOption.InitMaxStreams.class); + SCTP_INIT_MAXSTREAMS = + new SctpStdSocketOption( + "SCTP_INIT_MAXSTREAMS", SctpStandardSocketOptions.InitMaxStreams.class); /** * Enables or disables a Nagle-like algorithm. @@ -310,7 +310,7 @@ public class SctpStandardSocketOption { * This class is used to set the maximum number of inbound/outbound streams * used by the local endpoint during association initialization. An * instance of this class is used to set the {@link - * SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS SCTP_INIT_MAXSTREAMS} + * SctpStandardSocketOptions#SCTP_INIT_MAXSTREAMS SCTP_INIT_MAXSTREAMS} * socket option. * * @since 1.7 diff --git a/jdk/src/share/classes/java/net/SocketOption.java b/jdk/src/share/classes/java/net/SocketOption.java index 65dfe3c5a6b..d3c5972ee02 100644 --- a/jdk/src/share/classes/java/net/SocketOption.java +++ b/jdk/src/share/classes/java/net/SocketOption.java @@ -38,7 +38,7 @@ package java.net; * * @since 1.7 * - * @see StandardSocketOption + * @see StandardSocketOptions */ public interface SocketOption { diff --git a/jdk/src/share/classes/java/net/StandardSocketOption.java b/jdk/src/share/classes/java/net/StandardSocketOptions.java similarity index 99% rename from jdk/src/share/classes/java/net/StandardSocketOption.java rename to jdk/src/share/classes/java/net/StandardSocketOptions.java index bc9589689d3..b9bea8d0ab5 100644 --- a/jdk/src/share/classes/java/net/StandardSocketOption.java +++ b/jdk/src/share/classes/java/net/StandardSocketOptions.java @@ -38,8 +38,8 @@ package java.net; * @since 1.7 */ -public final class StandardSocketOption { - private StandardSocketOption() { } +public final class StandardSocketOptions { + private StandardSocketOptions() { } // -- SOL_SOCKET -- diff --git a/jdk/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java b/jdk/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java index c2b74d21c49..1b5a67f3c6d 100644 --- a/jdk/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java +++ b/jdk/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java @@ -58,11 +58,11 @@ import java.io.IOException; * Description * * - * {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} + * {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF} * The size of the socket receive buffer * * - * {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} + * {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} * Re-use address * * diff --git a/jdk/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java b/jdk/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java index 99692b38494..d78800b65d3 100644 --- a/jdk/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java +++ b/jdk/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java @@ -68,23 +68,23 @@ import java.nio.ByteBuffer; * Description * * - * {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} + * {@link java.net.StandardSocketOptions#SO_SNDBUF SO_SNDBUF} * The size of the socket send buffer * * - * {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} + * {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF} * The size of the socket receive buffer * * - * {@link java.net.StandardSocketOption#SO_KEEPALIVE SO_KEEPALIVE} + * {@link java.net.StandardSocketOptions#SO_KEEPALIVE SO_KEEPALIVE} * Keep connection alive * * - * {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} + * {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} * Re-use address * * - * {@link java.net.StandardSocketOption#TCP_NODELAY TCP_NODELAY} + * {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY} * Disable the Nagle algorithm * * diff --git a/jdk/src/share/classes/java/nio/channels/DatagramChannel.java b/jdk/src/share/classes/java/nio/channels/DatagramChannel.java index 0d99dc4e0d9..f4a6110b017 100644 --- a/jdk/src/share/classes/java/nio/channels/DatagramChannel.java +++ b/jdk/src/share/classes/java/nio/channels/DatagramChannel.java @@ -63,37 +63,37 @@ import java.nio.channels.spi.SelectorProvider; * Description * * - * {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} + * {@link java.net.StandardSocketOptions#SO_SNDBUF SO_SNDBUF} * The size of the socket send buffer * * - * {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} + * {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF} * The size of the socket receive buffer * * - * {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} + * {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} * Re-use address * * - * {@link java.net.StandardSocketOption#SO_BROADCAST SO_BROADCAST} + * {@link java.net.StandardSocketOptions#SO_BROADCAST SO_BROADCAST} * Allow transmission of broadcast datagrams * * - * {@link java.net.StandardSocketOption#IP_TOS IP_TOS} + * {@link java.net.StandardSocketOptions#IP_TOS IP_TOS} * The Type of Service (ToS) octet in the Internet Protocol (IP) header * * - * {@link java.net.StandardSocketOption#IP_MULTICAST_IF IP_MULTICAST_IF} + * {@link java.net.StandardSocketOptions#IP_MULTICAST_IF IP_MULTICAST_IF} * The network interface for Internet Protocol (IP) multicast datagrams * * - * {@link java.net.StandardSocketOption#IP_MULTICAST_TTL + * {@link java.net.StandardSocketOptions#IP_MULTICAST_TTL * IP_MULTICAST_TTL} * The time-to-live for Internet Protocol (IP) multicast * datagrams * * - * {@link java.net.StandardSocketOption#IP_MULTICAST_LOOP + * {@link java.net.StandardSocketOptions#IP_MULTICAST_LOOP * IP_MULTICAST_LOOP} * Loopback for Internet Protocol (IP) multicast datagrams * diff --git a/jdk/src/share/classes/java/nio/channels/MulticastChannel.java b/jdk/src/share/classes/java/nio/channels/MulticastChannel.java index d22f0d5a58e..5b61f77ff8a 100644 --- a/jdk/src/share/classes/java/nio/channels/MulticastChannel.java +++ b/jdk/src/share/classes/java/nio/channels/MulticastChannel.java @@ -30,7 +30,7 @@ import java.net.NetworkInterface; import java.io.IOException; import java.net.ProtocolFamily; // javadoc import java.net.StandardProtocolFamily; // javadoc -import java.net.StandardSocketOption; // javadoc +import java.net.StandardSocketOptions; // javadoc /** * A network channel that supports Internet Protocol (IP) multicasting. @@ -93,7 +93,7 @@ import java.net.StandardSocketOption; // javadoc * a specific address, rather than the wildcard address then it is implementation * specific if multicast datagrams are received by the socket.

  • * - *
  • The {@link StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} option should be + *

  • The {@link StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} option should be * enabled prior to {@link NetworkChannel#bind binding} the socket. This is * required to allow multiple members of the group to bind to the same * address.

  • @@ -107,9 +107,9 @@ import java.net.StandardSocketOption; // javadoc * NetworkInterface ni = NetworkInterface.getByName("hme0"); * * DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET) - * .setOption(StandardSocketOption.SO_REUSEADDR, true) + * .setOption(StandardSocketOptions.SO_REUSEADDR, true) * .bind(new InetSocketAddress(5000)) - * .setOption(StandardSocketOption.IP_MULTICAST_IF, ni); + * .setOption(StandardSocketOptions.IP_MULTICAST_IF, ni); * * InetAddress group = InetAddress.getByName("225.4.5.6"); * diff --git a/jdk/src/share/classes/java/nio/channels/NetworkChannel.java b/jdk/src/share/classes/java/nio/channels/NetworkChannel.java index f8a169d8c0a..4d4abfdb75c 100644 --- a/jdk/src/share/classes/java/nio/channels/NetworkChannel.java +++ b/jdk/src/share/classes/java/nio/channels/NetworkChannel.java @@ -124,7 +124,7 @@ public interface NetworkChannel * @throws IOException * If an I/O error occurs * - * @see java.net.StandardSocketOption + * @see java.net.StandardSocketOptions */ NetworkChannel setOption(SocketOption name, T value) throws IOException; @@ -144,7 +144,7 @@ public interface NetworkChannel * @throws IOException * If an I/O error occurs * - * @see java.net.StandardSocketOption + * @see java.net.StandardSocketOptions */ T getOption(SocketOption name) throws IOException; diff --git a/jdk/src/share/classes/java/nio/channels/ServerSocketChannel.java b/jdk/src/share/classes/java/nio/channels/ServerSocketChannel.java index 2711594a57e..127fb5bcd40 100644 --- a/jdk/src/share/classes/java/nio/channels/ServerSocketChannel.java +++ b/jdk/src/share/classes/java/nio/channels/ServerSocketChannel.java @@ -52,11 +52,11 @@ import java.nio.channels.spi.SelectorProvider; * Description * * - * {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} + * {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF} * The size of the socket receive buffer * * - * {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} + * {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} * Re-use address * * diff --git a/jdk/src/share/classes/java/nio/channels/SocketChannel.java b/jdk/src/share/classes/java/nio/channels/SocketChannel.java index 03ac42a3e94..d5f43ba7750 100644 --- a/jdk/src/share/classes/java/nio/channels/SocketChannel.java +++ b/jdk/src/share/classes/java/nio/channels/SocketChannel.java @@ -72,28 +72,28 @@ import java.nio.channels.spi.SelectorProvider; * Description * * - * {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} + * {@link java.net.StandardSocketOptions#SO_SNDBUF SO_SNDBUF} * The size of the socket send buffer * * - * {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} + * {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF} * The size of the socket receive buffer * * - * {@link java.net.StandardSocketOption#SO_KEEPALIVE SO_KEEPALIVE} + * {@link java.net.StandardSocketOptions#SO_KEEPALIVE SO_KEEPALIVE} * Keep connection alive * * - * {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} + * {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} * Re-use address * * - * {@link java.net.StandardSocketOption#SO_LINGER SO_LINGER} + * {@link java.net.StandardSocketOptions#SO_LINGER SO_LINGER} * Linger on close if data is present (when configured in blocking mode * only) * * - * {@link java.net.StandardSocketOption#TCP_NODELAY TCP_NODELAY} + * {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY} * Disable the Nagle algorithm * * diff --git a/jdk/src/share/classes/java/nio/file/Path.java b/jdk/src/share/classes/java/nio/file/Path.java index 69e5086a1e1..370c38dbac2 100644 --- a/jdk/src/share/classes/java/nio/file/Path.java +++ b/jdk/src/share/classes/java/nio/file/Path.java @@ -609,11 +609,11 @@ public interface Path * directory can be watched. The {@code events} parameter is the events to * register and may contain the following events: *
      - *
    • {@link StandardWatchEventKind#ENTRY_CREATE ENTRY_CREATE} - + *
    • {@link StandardWatchEventKinds#ENTRY_CREATE ENTRY_CREATE} - * entry created or moved into the directory
    • - *
    • {@link StandardWatchEventKind#ENTRY_DELETE ENTRY_DELETE} - + *
    • {@link StandardWatchEventKinds#ENTRY_DELETE ENTRY_DELETE} - * entry deleted or moved out of the directory
    • - *
    • {@link StandardWatchEventKind#ENTRY_MODIFY ENTRY_MODIFY} - + *
    • {@link StandardWatchEventKinds#ENTRY_MODIFY ENTRY_MODIFY} - * entry in directory was modified
    • *
    * @@ -622,7 +622,7 @@ public interface Path * that locates the directory entry that is created, deleted, or modified. * *

    The set of events may include additional implementation specific - * event that are not defined by the enum {@link StandardWatchEventKind} + * event that are not defined by the enum {@link StandardWatchEventKinds} * *

    The {@code modifiers} parameter specifies modifiers that * qualify how the directory is registered. This release does not define any diff --git a/jdk/src/share/classes/java/nio/file/StandardWatchEventKind.java b/jdk/src/share/classes/java/nio/file/StandardWatchEventKinds.java similarity index 94% rename from jdk/src/share/classes/java/nio/file/StandardWatchEventKind.java rename to jdk/src/share/classes/java/nio/file/StandardWatchEventKinds.java index 6064d75a7fa..ef111e2bf7d 100644 --- a/jdk/src/share/classes/java/nio/file/StandardWatchEventKind.java +++ b/jdk/src/share/classes/java/nio/file/StandardWatchEventKinds.java @@ -31,8 +31,8 @@ package java.nio.file; * @since 1.7 */ -public final class StandardWatchEventKind { - private StandardWatchEventKind() { } +public final class StandardWatchEventKinds { + private StandardWatchEventKinds() { } /** * A special event to indicate that events may have been lost or @@ -44,8 +44,8 @@ public final class StandardWatchEventKind { * * @see WatchService */ - public static final WatchEvent.Kind OVERFLOW = - new StdWatchEventKind("OVERFLOW", Void.class); + public static final WatchEvent.Kind OVERFLOW = + new StdWatchEventKind("OVERFLOW", Object.class); /** * Directory entry created. diff --git a/jdk/src/share/classes/java/nio/file/WatchEvent.java b/jdk/src/share/classes/java/nio/file/WatchEvent.java index 4328828fe22..cab199f0b57 100644 --- a/jdk/src/share/classes/java/nio/file/WatchEvent.java +++ b/jdk/src/share/classes/java/nio/file/WatchEvent.java @@ -50,7 +50,7 @@ public interface WatchEvent { * An event kind, for the purposes of identification. * * @since 1.7 - * @see StandardWatchEventKind + * @see StandardWatchEventKinds */ public static interface Kind { /** @@ -98,9 +98,9 @@ public interface WatchEvent { /** * Returns the context for the event. * - *

    In the case of {@link StandardWatchEventKind#ENTRY_CREATE ENTRY_CREATE}, - * {@link StandardWatchEventKind#ENTRY_DELETE ENTRY_DELETE}, and {@link - * StandardWatchEventKind#ENTRY_MODIFY ENTRY_MODIFY} events the context is + *

    In the case of {@link StandardWatchEventKinds#ENTRY_CREATE ENTRY_CREATE}, + * {@link StandardWatchEventKinds#ENTRY_DELETE ENTRY_DELETE}, and {@link + * StandardWatchEventKinds#ENTRY_MODIFY ENTRY_MODIFY} events the context is * a {@code Path} that is the {@link Path#relativize relative} path between * the directory registered with the watch service, and the entry that is * created, deleted, or modified. diff --git a/jdk/src/share/classes/java/nio/file/WatchService.java b/jdk/src/share/classes/java/nio/file/WatchService.java index 58e633610c0..5a63fcd8722 100644 --- a/jdk/src/share/classes/java/nio/file/WatchService.java +++ b/jdk/src/share/classes/java/nio/file/WatchService.java @@ -68,7 +68,7 @@ import java.util.concurrent.TimeUnit; * of events that it may accumulate. Where an implementation knowingly * discards events then it arranges for the key's {@link WatchKey#pollEvents * pollEvents} method to return an element with an event type of {@link - * StandardWatchEventKind#OVERFLOW OVERFLOW}. This event can be used by the + * StandardWatchEventKinds#OVERFLOW OVERFLOW}. This event can be used by the * consumer as a trigger to re-examine the state of the object. * *

    When an event is reported to indicate that a file in a watched directory @@ -87,7 +87,7 @@ import java.util.concurrent.TimeUnit; * are detected, their timeliness, and whether their ordering is preserved are * highly implementation specific. For example, when a file in a watched * directory is modified then it may result in a single {@link - * StandardWatchEventKind#ENTRY_MODIFY ENTRY_MODIFY} event in some + * StandardWatchEventKinds#ENTRY_MODIFY ENTRY_MODIFY} event in some * implementations but several events in other implementations. Short-lived * files (meaning files that are deleted very quickly after they are created) * may not be detected by primitive implementations that periodically poll the diff --git a/jdk/src/share/classes/java/nio/file/Watchable.java b/jdk/src/share/classes/java/nio/file/Watchable.java index 41ef35200e7..f84437c5c69 100644 --- a/jdk/src/share/classes/java/nio/file/Watchable.java +++ b/jdk/src/share/classes/java/nio/file/Watchable.java @@ -53,7 +53,7 @@ public interface Watchable { * those specified by the {@code events} and {@code modifiers} parameters. * Changing the event set does not cause pending events for the object to be * discarded. Objects are automatically registered for the {@link - * StandardWatchEventKind#OVERFLOW OVERFLOW} event. This event is not + * StandardWatchEventKinds#OVERFLOW OVERFLOW} event. This event is not * required to be present in the array of events. * *

    Otherwise the file system object has not yet been registered with the diff --git a/jdk/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java index 2f80a8d1f82..73590399d13 100644 --- a/jdk/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java @@ -28,7 +28,7 @@ package sun.nio.ch; import java.nio.channels.*; import java.net.SocketAddress; import java.net.SocketOption; -import java.net.StandardSocketOption; +import java.net.StandardSocketOptions; import java.net.InetSocketAddress; import java.io.FileDescriptor; import java.io.IOException; @@ -214,8 +214,8 @@ abstract class AsynchronousServerSocketChannelImpl private static Set> defaultOptions() { HashSet> set = new HashSet>(2); - set.add(StandardSocketOption.SO_RCVBUF); - set.add(StandardSocketOption.SO_REUSEADDR); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); return Collections.unmodifiableSet(set); } } diff --git a/jdk/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java index 4f5f65903f1..c1ac6e75c77 100644 --- a/jdk/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java @@ -28,7 +28,7 @@ package sun.nio.ch; import java.nio.ByteBuffer; import java.nio.channels.*; import java.net.SocketOption; -import java.net.StandardSocketOption; +import java.net.StandardSocketOptions; import java.net.SocketAddress; import java.net.InetSocketAddress; import java.io.IOException; @@ -483,11 +483,11 @@ abstract class AsynchronousSocketChannelImpl private static Set> defaultOptions() { HashSet> set = new HashSet>(5); - set.add(StandardSocketOption.SO_SNDBUF); - set.add(StandardSocketOption.SO_RCVBUF); - set.add(StandardSocketOption.SO_KEEPALIVE); - set.add(StandardSocketOption.SO_REUSEADDR); - set.add(StandardSocketOption.TCP_NODELAY); + set.add(StandardSocketOptions.SO_SNDBUF); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_KEEPALIVE); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.TCP_NODELAY); return Collections.unmodifiableSet(set); } } diff --git a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java index 2a0c0eb5257..bdc502216c2 100644 --- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -188,7 +188,7 @@ class DatagramChannelImpl synchronized (stateLock) { ensureOpen(); - if (name == StandardSocketOption.IP_TOS) { + if (name == StandardSocketOptions.IP_TOS) { // IPv4 only; no-op for IPv6 if (family == StandardProtocolFamily.INET) { Net.setSocketOption(fd, family, name, value); @@ -196,15 +196,15 @@ class DatagramChannelImpl return this; } - if (name == StandardSocketOption.IP_MULTICAST_TTL || - name == StandardSocketOption.IP_MULTICAST_LOOP) + if (name == StandardSocketOptions.IP_MULTICAST_TTL || + name == StandardSocketOptions.IP_MULTICAST_LOOP) { // options are protocol dependent Net.setSocketOption(fd, family, name, value); return this; } - if (name == StandardSocketOption.IP_MULTICAST_IF) { + if (name == StandardSocketOptions.IP_MULTICAST_IF) { if (value == null) throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'"); NetworkInterface interf = (NetworkInterface)value; @@ -243,7 +243,7 @@ class DatagramChannelImpl synchronized (stateLock) { ensureOpen(); - if (name == StandardSocketOption.IP_TOS) { + if (name == StandardSocketOptions.IP_TOS) { // IPv4 only; always return 0 on IPv6 if (family == StandardProtocolFamily.INET) { return (T) Net.getSocketOption(fd, family, name); @@ -252,13 +252,13 @@ class DatagramChannelImpl } } - if (name == StandardSocketOption.IP_MULTICAST_TTL || - name == StandardSocketOption.IP_MULTICAST_LOOP) + if (name == StandardSocketOptions.IP_MULTICAST_TTL || + name == StandardSocketOptions.IP_MULTICAST_LOOP) { return (T) Net.getSocketOption(fd, family, name); } - if (name == StandardSocketOption.IP_MULTICAST_IF) { + if (name == StandardSocketOptions.IP_MULTICAST_IF) { if (family == StandardProtocolFamily.INET) { int address = Net.getInterface4(fd); if (address == 0) @@ -291,14 +291,14 @@ class DatagramChannelImpl private static Set> defaultOptions() { HashSet> set = new HashSet>(8); - set.add(StandardSocketOption.SO_SNDBUF); - set.add(StandardSocketOption.SO_RCVBUF); - set.add(StandardSocketOption.SO_REUSEADDR); - set.add(StandardSocketOption.SO_BROADCAST); - set.add(StandardSocketOption.IP_TOS); - set.add(StandardSocketOption.IP_MULTICAST_IF); - set.add(StandardSocketOption.IP_MULTICAST_TTL); - set.add(StandardSocketOption.IP_MULTICAST_LOOP); + set.add(StandardSocketOptions.SO_SNDBUF); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.SO_BROADCAST); + set.add(StandardSocketOptions.IP_TOS); + set.add(StandardSocketOptions.IP_MULTICAST_IF); + set.add(StandardSocketOptions.IP_MULTICAST_TTL); + set.add(StandardSocketOptions.IP_MULTICAST_LOOP); return Collections.unmodifiableSet(set); } } diff --git a/jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java b/jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java index 37ff489a0cb..7efdb8f434a 100644 --- a/jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java +++ b/jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java @@ -312,46 +312,46 @@ public class DatagramSocketAdaptor public void setSendBufferSize(int size) throws SocketException { if (size <= 0) throw new IllegalArgumentException("Invalid send size"); - setIntOption(StandardSocketOption.SO_SNDBUF, size); + setIntOption(StandardSocketOptions.SO_SNDBUF, size); } public int getSendBufferSize() throws SocketException { - return getIntOption(StandardSocketOption.SO_SNDBUF); + return getIntOption(StandardSocketOptions.SO_SNDBUF); } public void setReceiveBufferSize(int size) throws SocketException { if (size <= 0) throw new IllegalArgumentException("Invalid receive size"); - setIntOption(StandardSocketOption.SO_RCVBUF, size); + setIntOption(StandardSocketOptions.SO_RCVBUF, size); } public int getReceiveBufferSize() throws SocketException { - return getIntOption(StandardSocketOption.SO_RCVBUF); + return getIntOption(StandardSocketOptions.SO_RCVBUF); } public void setReuseAddress(boolean on) throws SocketException { - setBooleanOption(StandardSocketOption.SO_REUSEADDR, on); + setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); } public boolean getReuseAddress() throws SocketException { - return getBooleanOption(StandardSocketOption.SO_REUSEADDR); + return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); } public void setBroadcast(boolean on) throws SocketException { - setBooleanOption(StandardSocketOption.SO_BROADCAST, on); + setBooleanOption(StandardSocketOptions.SO_BROADCAST, on); } public boolean getBroadcast() throws SocketException { - return getBooleanOption(StandardSocketOption.SO_BROADCAST); + return getBooleanOption(StandardSocketOptions.SO_BROADCAST); } public void setTrafficClass(int tc) throws SocketException { - setIntOption(StandardSocketOption.IP_TOS, tc); + setIntOption(StandardSocketOptions.IP_TOS, tc); } public int getTrafficClass() throws SocketException { - return getIntOption(StandardSocketOption.IP_TOS); + return getIntOption(StandardSocketOptions.IP_TOS); } public void close() { diff --git a/jdk/src/share/classes/sun/nio/ch/ExtendedSocketOption.java b/jdk/src/share/classes/sun/nio/ch/ExtendedSocketOption.java index b384afd347b..c5f2563b723 100644 --- a/jdk/src/share/classes/sun/nio/ch/ExtendedSocketOption.java +++ b/jdk/src/share/classes/sun/nio/ch/ExtendedSocketOption.java @@ -29,7 +29,7 @@ import java.net.SocketOption; /** * Defines socket options that are supported by the implementation - * but not defined in StandardSocketOption. + * but not defined in StandardSocketOptions. */ class ExtendedSocketOption { diff --git a/jdk/src/share/classes/sun/nio/ch/Net.java b/jdk/src/share/classes/sun/nio/ch/Net.java index a7cefd842a8..aecbc7ba5bc 100644 --- a/jdk/src/share/classes/sun/nio/ch/Net.java +++ b/jdk/src/share/classes/sun/nio/ch/Net.java @@ -237,26 +237,26 @@ class Net { // package-private throw new AssertionError("Should not reach here"); // special handling - if (name == StandardSocketOption.SO_RCVBUF || - name == StandardSocketOption.SO_SNDBUF) + if (name == StandardSocketOptions.SO_RCVBUF || + name == StandardSocketOptions.SO_SNDBUF) { int i = ((Integer)value).intValue(); if (i < 0) throw new IllegalArgumentException("Invalid send/receive buffer size"); } - if (name == StandardSocketOption.SO_LINGER) { + if (name == StandardSocketOptions.SO_LINGER) { int i = ((Integer)value).intValue(); if (i < 0) value = Integer.valueOf(-1); if (i > 65535) value = Integer.valueOf(65535); } - if (name == StandardSocketOption.IP_TOS) { + if (name == StandardSocketOptions.IP_TOS) { int i = ((Integer)value).intValue(); if (i < 0 || i > 255) throw new IllegalArgumentException("Invalid IP_TOS value"); } - if (name == StandardSocketOption.IP_MULTICAST_TTL) { + if (name == StandardSocketOptions.IP_MULTICAST_TTL) { int i = ((Integer)value).intValue(); if (i < 0 || i > 255) throw new IllegalArgumentException("Invalid TTL/hop value"); diff --git a/jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java b/jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java index 4fb8330e7b9..f897d3460db 100644 --- a/jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java +++ b/jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java @@ -169,7 +169,7 @@ public class ServerSocketAdaptor // package-private public void setReuseAddress(boolean on) throws SocketException { try { - ssc.setOption(StandardSocketOption.SO_REUSEADDR, on); + ssc.setOption(StandardSocketOptions.SO_REUSEADDR, on); } catch (IOException x) { Net.translateToSocketException(x); } @@ -177,7 +177,7 @@ public class ServerSocketAdaptor // package-private public boolean getReuseAddress() throws SocketException { try { - return ssc.getOption(StandardSocketOption.SO_REUSEADDR).booleanValue(); + return ssc.getOption(StandardSocketOptions.SO_REUSEADDR).booleanValue(); } catch (IOException x) { Net.translateToSocketException(x); return false; // Never happens @@ -197,7 +197,7 @@ public class ServerSocketAdaptor // package-private if (size <= 0) throw new IllegalArgumentException("size cannot be 0 or negative"); try { - ssc.setOption(StandardSocketOption.SO_RCVBUF, size); + ssc.setOption(StandardSocketOptions.SO_RCVBUF, size); } catch (IOException x) { Net.translateToSocketException(x); } @@ -205,7 +205,7 @@ public class ServerSocketAdaptor // package-private public int getReceiveBufferSize() throws SocketException { try { - return ssc.getOption(StandardSocketOption.SO_RCVBUF).intValue(); + return ssc.getOption(StandardSocketOptions.SO_RCVBUF).intValue(); } catch (IOException x) { Net.translateToSocketException(x); return -1; // Never happens diff --git a/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java index a9000bcd34f..671fad27733 100644 --- a/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java @@ -160,8 +160,8 @@ class ServerSocketChannelImpl private static Set> defaultOptions() { HashSet> set = new HashSet>(2); - set.add(StandardSocketOption.SO_RCVBUF); - set.add(StandardSocketOption.SO_REUSEADDR); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); return Collections.unmodifiableSet(set); } } diff --git a/jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java b/jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java index c2f7217b7df..dd10a721073 100644 --- a/jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java +++ b/jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java @@ -318,21 +318,21 @@ public class SocketAdaptor } public void setTcpNoDelay(boolean on) throws SocketException { - setBooleanOption(StandardSocketOption.TCP_NODELAY, on); + setBooleanOption(StandardSocketOptions.TCP_NODELAY, on); } public boolean getTcpNoDelay() throws SocketException { - return getBooleanOption(StandardSocketOption.TCP_NODELAY); + return getBooleanOption(StandardSocketOptions.TCP_NODELAY); } public void setSoLinger(boolean on, int linger) throws SocketException { if (!on) linger = -1; - setIntOption(StandardSocketOption.SO_LINGER, linger); + setIntOption(StandardSocketOptions.SO_LINGER, linger); } public int getSoLinger() throws SocketException { - return getIntOption(StandardSocketOption.SO_LINGER); + return getIntOption(StandardSocketOptions.SO_LINGER); } public void sendUrgentData(int data) throws IOException { @@ -366,46 +366,46 @@ public class SocketAdaptor // size 0 valid for SocketChannel, invalid for Socket if (size <= 0) throw new IllegalArgumentException("Invalid send size"); - setIntOption(StandardSocketOption.SO_SNDBUF, size); + setIntOption(StandardSocketOptions.SO_SNDBUF, size); } public int getSendBufferSize() throws SocketException { - return getIntOption(StandardSocketOption.SO_SNDBUF); + return getIntOption(StandardSocketOptions.SO_SNDBUF); } public void setReceiveBufferSize(int size) throws SocketException { // size 0 valid for SocketChannel, invalid for Socket if (size <= 0) throw new IllegalArgumentException("Invalid receive size"); - setIntOption(StandardSocketOption.SO_RCVBUF, size); + setIntOption(StandardSocketOptions.SO_RCVBUF, size); } public int getReceiveBufferSize() throws SocketException { - return getIntOption(StandardSocketOption.SO_RCVBUF); + return getIntOption(StandardSocketOptions.SO_RCVBUF); } public void setKeepAlive(boolean on) throws SocketException { - setBooleanOption(StandardSocketOption.SO_KEEPALIVE, on); + setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on); } public boolean getKeepAlive() throws SocketException { - return getBooleanOption(StandardSocketOption.SO_KEEPALIVE); + return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE); } public void setTrafficClass(int tc) throws SocketException { - setIntOption(StandardSocketOption.IP_TOS, tc); + setIntOption(StandardSocketOptions.IP_TOS, tc); } public int getTrafficClass() throws SocketException { - return getIntOption(StandardSocketOption.IP_TOS); + return getIntOption(StandardSocketOptions.IP_TOS); } public void setReuseAddress(boolean on) throws SocketException { - setBooleanOption(StandardSocketOption.SO_REUSEADDR, on); + setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); } public boolean getReuseAddress() throws SocketException { - return getBooleanOption(StandardSocketOption.SO_REUSEADDR); + return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); } public void close() throws IOException { diff --git a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java index 8540229c36b..ba55ab1b0ed 100644 --- a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -170,7 +170,7 @@ class SocketChannelImpl throw new ClosedChannelException(); // special handling for IP_TOS: no-op when IPv6 - if (name == StandardSocketOption.IP_TOS) { + if (name == StandardSocketOptions.IP_TOS) { if (!Net.isIPv6Available()) Net.setSocketOption(fd, StandardProtocolFamily.INET, name, value); return this; @@ -197,7 +197,7 @@ class SocketChannelImpl throw new ClosedChannelException(); // special handling for IP_TOS: always return 0 when IPv6 - if (name == StandardSocketOption.IP_TOS) { + if (name == StandardSocketOptions.IP_TOS) { return (Net.isIPv6Available()) ? (T) Integer.valueOf(0) : (T) Net.getSocketOption(fd, StandardProtocolFamily.INET, name); } @@ -212,14 +212,14 @@ class SocketChannelImpl private static Set> defaultOptions() { HashSet> set = new HashSet>(8); - set.add(StandardSocketOption.SO_SNDBUF); - set.add(StandardSocketOption.SO_RCVBUF); - set.add(StandardSocketOption.SO_KEEPALIVE); - set.add(StandardSocketOption.SO_REUSEADDR); - set.add(StandardSocketOption.SO_LINGER); - set.add(StandardSocketOption.TCP_NODELAY); + set.add(StandardSocketOptions.SO_SNDBUF); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_KEEPALIVE); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.SO_LINGER); + set.add(StandardSocketOptions.TCP_NODELAY); // additional options required by socket adaptor - set.add(StandardSocketOption.IP_TOS); + set.add(StandardSocketOptions.IP_TOS); set.add(ExtendedSocketOption.SO_OOBINLINE); return Collections.unmodifiableSet(set); } diff --git a/jdk/src/share/classes/sun/nio/fs/AbstractPoller.java b/jdk/src/share/classes/sun/nio/fs/AbstractPoller.java index 76664a3ff55..fd3503ce26c 100644 --- a/jdk/src/share/classes/sun/nio/fs/AbstractPoller.java +++ b/jdk/src/share/classes/sun/nio/fs/AbstractPoller.java @@ -105,16 +105,16 @@ abstract class AbstractPoller implements Runnable { Set> eventSet = new HashSet<>(events.length); for (WatchEvent.Kind event: events) { // standard events - if (event == StandardWatchEventKind.ENTRY_CREATE || - event == StandardWatchEventKind.ENTRY_MODIFY || - event == StandardWatchEventKind.ENTRY_DELETE) + if (event == StandardWatchEventKinds.ENTRY_CREATE || + event == StandardWatchEventKinds.ENTRY_MODIFY || + event == StandardWatchEventKinds.ENTRY_DELETE) { eventSet.add(event); continue; } // OVERFLOW is ignored - if (event == StandardWatchEventKind.OVERFLOW) { + if (event == StandardWatchEventKinds.OVERFLOW) { if (events.length == 1) throw new IllegalArgumentException("No events to register"); continue; diff --git a/jdk/src/share/classes/sun/nio/fs/AbstractWatchKey.java b/jdk/src/share/classes/sun/nio/fs/AbstractWatchKey.java index 6e06ab73c8c..132b6e5d845 100644 --- a/jdk/src/share/classes/sun/nio/fs/AbstractWatchKey.java +++ b/jdk/src/share/classes/sun/nio/fs/AbstractWatchKey.java @@ -42,8 +42,8 @@ abstract class AbstractWatchKey implements WatchKey { /** * Special event to signal overflow */ - static final Event OVERFLOW_EVENT = - new Event(StandardWatchEventKind.OVERFLOW, null); + static final Event OVERFLOW_EVENT = + new Event(StandardWatchEventKinds.OVERFLOW, null); /** * Possible key states @@ -103,14 +103,14 @@ abstract class AbstractWatchKey implements WatchKey { */ @SuppressWarnings("unchecked") final void signalEvent(WatchEvent.Kind kind, Object context) { - boolean isModify = (kind == StandardWatchEventKind.ENTRY_MODIFY); + boolean isModify = (kind == StandardWatchEventKinds.ENTRY_MODIFY); synchronized (this) { int size = events.size(); if (size > 0) { // if the previous event is an OVERFLOW event or this is a // repeated event then we simply increment the counter WatchEvent prev = events.get(size-1); - if ((prev.kind() == StandardWatchEventKind.OVERFLOW) || + if ((prev.kind() == StandardWatchEventKinds.OVERFLOW) || ((kind == prev.kind() && Objects.equals(context, prev.context())))) { @@ -124,7 +124,7 @@ abstract class AbstractWatchKey implements WatchKey { if (isModify) { WatchEvent ev = lastModifyEvents.get(context); if (ev != null) { - assert ev.kind() == StandardWatchEventKind.ENTRY_MODIFY; + assert ev.kind() == StandardWatchEventKinds.ENTRY_MODIFY; ((Event)ev).increment(); return; } @@ -138,7 +138,7 @@ abstract class AbstractWatchKey implements WatchKey { // if the list has reached the limit then drop pending events // and queue an OVERFLOW event if (size >= MAX_EVENT_LIST_SIZE) { - kind = StandardWatchEventKind.OVERFLOW; + kind = StandardWatchEventKinds.OVERFLOW; isModify = false; context = null; } @@ -149,7 +149,7 @@ abstract class AbstractWatchKey implements WatchKey { new Event((WatchEvent.Kind)kind, context); if (isModify) { lastModifyEvents.put(context, ev); - } else if (kind == StandardWatchEventKind.OVERFLOW) { + } else if (kind == StandardWatchEventKinds.OVERFLOW) { // drop all pending events events.clear(); lastModifyEvents.clear(); diff --git a/jdk/src/share/classes/sun/nio/fs/PollingWatchService.java b/jdk/src/share/classes/sun/nio/fs/PollingWatchService.java index 20f59914e62..30de2473bee 100644 --- a/jdk/src/share/classes/sun/nio/fs/PollingWatchService.java +++ b/jdk/src/share/classes/sun/nio/fs/PollingWatchService.java @@ -80,16 +80,16 @@ class PollingWatchService new HashSet>(events.length); for (WatchEvent.Kind event: events) { // standard events - if (event == StandardWatchEventKind.ENTRY_CREATE || - event == StandardWatchEventKind.ENTRY_MODIFY || - event == StandardWatchEventKind.ENTRY_DELETE) + if (event == StandardWatchEventKinds.ENTRY_CREATE || + event == StandardWatchEventKinds.ENTRY_MODIFY || + event == StandardWatchEventKinds.ENTRY_DELETE) { eventSet.add(event); continue; } // OVERFLOW is ignored - if (event == StandardWatchEventKind.OVERFLOW) { + if (event == StandardWatchEventKinds.OVERFLOW) { if (events.length == 1) throw new IllegalArgumentException("No events to register"); continue; @@ -355,16 +355,16 @@ class PollingWatchService new CacheEntry(lastModified, tickCount)); // queue ENTRY_CREATE if event enabled - if (events.contains(StandardWatchEventKind.ENTRY_CREATE)) { - signalEvent(StandardWatchEventKind.ENTRY_CREATE, entry.getFileName()); + if (events.contains(StandardWatchEventKinds.ENTRY_CREATE)) { + signalEvent(StandardWatchEventKinds.ENTRY_CREATE, entry.getFileName()); continue; } else { // if ENTRY_CREATE is not enabled and ENTRY_MODIFY is // enabled then queue event to avoid missing out on // modifications to the file immediately after it is // created. - if (events.contains(StandardWatchEventKind.ENTRY_MODIFY)) { - signalEvent(StandardWatchEventKind.ENTRY_MODIFY, entry.getFileName()); + if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) { + signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, entry.getFileName()); } } continue; @@ -372,8 +372,8 @@ class PollingWatchService // check if file has changed if (e.lastModified != lastModified) { - if (events.contains(StandardWatchEventKind.ENTRY_MODIFY)) { - signalEvent(StandardWatchEventKind.ENTRY_MODIFY, + if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) { + signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, entry.getFileName()); } } @@ -403,8 +403,8 @@ class PollingWatchService Path name = mapEntry.getKey(); // remove from map and queue delete event (if enabled) i.remove(); - if (events.contains(StandardWatchEventKind.ENTRY_DELETE)) { - signalEvent(StandardWatchEventKind.ENTRY_DELETE, name); + if (events.contains(StandardWatchEventKinds.ENTRY_DELETE)) { + signalEvent(StandardWatchEventKinds.ENTRY_DELETE, name); } } } diff --git a/jdk/src/share/native/sun/nio/ch/genSocketOptionRegistry.c b/jdk/src/share/native/sun/nio/ch/genSocketOptionRegistry.c index b5ed8f398bc..1bb2f700b41 100644 --- a/jdk/src/share/native/sun/nio/ch/genSocketOptionRegistry.c +++ b/jdk/src/share/native/sun/nio/ch/genSocketOptionRegistry.c @@ -63,7 +63,7 @@ int main(int argc, const char* argv[]) { out("// AUTOMATICALLY GENERATED FILE - DO NOT EDIT "); out("package sun.nio.ch; "); out("import java.net.SocketOption; "); - out("import java.net.StandardSocketOption; "); + out("import java.net.StandardSocketOptions; "); out("import java.net.ProtocolFamily; "); out("import java.net.StandardProtocolFamily; "); out("import java.util.Map; "); @@ -73,7 +73,7 @@ int main(int argc, const char* argv[]) { out(" private static class RegistryKey { "); out(" private final SocketOption name; "); out(" private final ProtocolFamily family; "); - out(" RegistryKey(SocketOption name, ProtocolFamily family) { "); + out(" RegistryKey(SocketOption name, ProtocolFamily family) { "); out(" this.name = name; "); out(" this.family = family; "); out(" } "); @@ -95,23 +95,23 @@ int main(int argc, const char* argv[]) { out(" Map map = "); out(" new HashMap(); "); - emit_unspec("StandardSocketOption.SO_BROADCAST", SOL_SOCKET, SO_BROADCAST); - emit_unspec("StandardSocketOption.SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE); - emit_unspec("StandardSocketOption.SO_LINGER", SOL_SOCKET, SO_LINGER); - emit_unspec("StandardSocketOption.SO_SNDBUF", SOL_SOCKET, SO_SNDBUF); - emit_unspec("StandardSocketOption.SO_RCVBUF", SOL_SOCKET, SO_RCVBUF); - emit_unspec("StandardSocketOption.SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR); - emit_unspec("StandardSocketOption.TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY); + emit_unspec("StandardSocketOptions.SO_BROADCAST", SOL_SOCKET, SO_BROADCAST); + emit_unspec("StandardSocketOptions.SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE); + emit_unspec("StandardSocketOptions.SO_LINGER", SOL_SOCKET, SO_LINGER); + emit_unspec("StandardSocketOptions.SO_SNDBUF", SOL_SOCKET, SO_SNDBUF); + emit_unspec("StandardSocketOptions.SO_RCVBUF", SOL_SOCKET, SO_RCVBUF); + emit_unspec("StandardSocketOptions.SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR); + emit_unspec("StandardSocketOptions.TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY); - emit_inet("StandardSocketOption.IP_TOS", IPPROTO_IP, IP_TOS); - emit_inet("StandardSocketOption.IP_MULTICAST_IF", IPPROTO_IP, IP_MULTICAST_IF); - emit_inet("StandardSocketOption.IP_MULTICAST_TTL", IPPROTO_IP, IP_MULTICAST_TTL); - emit_inet("StandardSocketOption.IP_MULTICAST_LOOP", IPPROTO_IP, IP_MULTICAST_LOOP); + emit_inet("StandardSocketOptions.IP_TOS", IPPROTO_IP, IP_TOS); + emit_inet("StandardSocketOptions.IP_MULTICAST_IF", IPPROTO_IP, IP_MULTICAST_IF); + emit_inet("StandardSocketOptions.IP_MULTICAST_TTL", IPPROTO_IP, IP_MULTICAST_TTL); + emit_inet("StandardSocketOptions.IP_MULTICAST_LOOP", IPPROTO_IP, IP_MULTICAST_LOOP); #ifdef AF_INET6 - emit_inet6("StandardSocketOption.IP_MULTICAST_IF", IPPROTO_IPV6, IPV6_MULTICAST_IF); - emit_inet6("StandardSocketOption.IP_MULTICAST_TTL", IPPROTO_IPV6, IPV6_MULTICAST_HOPS); - emit_inet6("StandardSocketOption.IP_MULTICAST_LOOP", IPPROTO_IPV6, IPV6_MULTICAST_LOOP); + emit_inet6("StandardSocketOptions.IP_MULTICAST_IF", IPPROTO_IPV6, IPV6_MULTICAST_IF); + emit_inet6("StandardSocketOptions.IP_MULTICAST_TTL", IPPROTO_IPV6, IPV6_MULTICAST_HOPS); + emit_inet6("StandardSocketOptions.IP_MULTICAST_LOOP", IPPROTO_IPV6, IPV6_MULTICAST_LOOP); #endif emit_unspec("ExtendedSocketOption.SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE); diff --git a/jdk/src/share/sample/nio/chatserver/ChatServer.java b/jdk/src/share/sample/nio/chatserver/ChatServer.java index 09d7c26b868..072999bd511 100644 --- a/jdk/src/share/sample/nio/chatserver/ChatServer.java +++ b/jdk/src/share/sample/nio/chatserver/ChatServer.java @@ -32,7 +32,7 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.net.StandardSocketOption; +import java.net.StandardSocketOptions; import java.nio.channels.*; import java.util.*; import java.util.concurrent.Executors; @@ -105,7 +105,7 @@ public class ChatServer implements Runnable { */ private AsynchronousServerSocketChannel createListener(AsynchronousChannelGroup channelGroup) throws IOException { final AsynchronousServerSocketChannel listener = openChannel(channelGroup); - listener.setOption(StandardSocketOption.SO_REUSEADDR, true); + listener.setOption(StandardSocketOptions.SO_REUSEADDR, true); listener.bind(new InetSocketAddress(port)); return listener; } @@ -123,7 +123,7 @@ public class ChatServer implements Runnable { private void handleNewConnection(AsynchronousSocketChannel channel) { Client client = new Client(channel, new ClientReader(this, new NameReader(this))); try { - channel.setOption(StandardSocketOption.TCP_NODELAY, true); + channel.setOption(StandardSocketOptions.TCP_NODELAY, true); } catch (IOException e) { // ignore } diff --git a/jdk/src/share/sample/nio/file/WatchDir.java b/jdk/src/share/sample/nio/file/WatchDir.java index 9b9d2ece052..49e278e14ef 100644 --- a/jdk/src/share/sample/nio/file/WatchDir.java +++ b/jdk/src/share/sample/nio/file/WatchDir.java @@ -30,7 +30,7 @@ */ import java.nio.file.*; -import static java.nio.file.StandardWatchEventKind.*; +import static java.nio.file.StandardWatchEventKinds.*; import static java.nio.file.LinkOption.*; import java.nio.file.attribute.*; import java.io.IOException; diff --git a/jdk/src/share/sample/nio/multicast/Reader.java b/jdk/src/share/sample/nio/multicast/Reader.java index 32b3c384c0d..59190f5b8e9 100644 --- a/jdk/src/share/sample/nio/multicast/Reader.java +++ b/jdk/src/share/sample/nio/multicast/Reader.java @@ -96,7 +96,7 @@ public class Reader { family = StandardProtocolFamily.INET6; } DatagramChannel dc = DatagramChannel.open(family) - .setOption(StandardSocketOption.SO_REUSEADDR, true) + .setOption(StandardSocketOptions.SO_REUSEADDR, true) .bind(new InetSocketAddress(target.port())); if (includeList.isEmpty()) { diff --git a/jdk/src/share/sample/nio/multicast/Sender.java b/jdk/src/share/sample/nio/multicast/Sender.java index 925a4be5ac6..d22d334f604 100644 --- a/jdk/src/share/sample/nio/multicast/Sender.java +++ b/jdk/src/share/sample/nio/multicast/Sender.java @@ -59,7 +59,7 @@ public class Sender { family = StandardProtocolFamily.INET6; DatagramChannel dc = DatagramChannel.open(family).bind(new InetSocketAddress(0)); if (target.interf() != null) { - dc.setOption(StandardSocketOption.IP_MULTICAST_IF, target.interf()); + dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, target.interf()); } // send multicast packet diff --git a/jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java b/jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java index 342ecb06ec4..5d961d4e5dd 100644 --- a/jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java +++ b/jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java @@ -55,7 +55,7 @@ import com.sun.nio.sctp.SctpChannel; import com.sun.nio.sctp.SctpSocketOption; import sun.nio.ch.PollArrayWrapper; import sun.nio.ch.SelChImpl; -import static com.sun.nio.sctp.SctpStandardSocketOption.*; +import static com.sun.nio.sctp.SctpStandardSocketOptions.*; import static sun.nio.ch.SctpResultContainer.SEND_FAILED; import static sun.nio.ch.SctpResultContainer.ASSOCIATION_CHANGED; import static sun.nio.ch.SctpResultContainer.PEER_ADDRESS_CHANGED; diff --git a/jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java b/jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java index 028d76eb571..00f82121fc9 100644 --- a/jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java +++ b/jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java @@ -53,7 +53,7 @@ import com.sun.nio.sctp.MessageInfo; import com.sun.nio.sctp.SctpChannel; import com.sun.nio.sctp.SctpMultiChannel; import com.sun.nio.sctp.SctpSocketOption; -import static com.sun.nio.sctp.SctpStandardSocketOption.*; +import static com.sun.nio.sctp.SctpStandardSocketOptions.*; import static sun.nio.ch.SctpResultContainer.*; /** diff --git a/jdk/src/solaris/classes/sun/nio/ch/SctpNet.java b/jdk/src/solaris/classes/sun/nio/ch/SctpNet.java index f464ae6c95f..322d973bcf4 100644 --- a/jdk/src/solaris/classes/sun/nio/ch/SctpNet.java +++ b/jdk/src/solaris/classes/sun/nio/ch/SctpNet.java @@ -35,7 +35,7 @@ import java.util.HashSet; import java.security.AccessController; import sun.security.action.GetPropertyAction; import com.sun.nio.sctp.SctpSocketOption; -import static com.sun.nio.sctp.SctpStandardSocketOption.*; +import static com.sun.nio.sctp.SctpStandardSocketOptions.*; public class SctpNet { static final String osName = AccessController.doPrivileged( diff --git a/jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java b/jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java index d1e3287ead2..845ac8ee7ba 100644 --- a/jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java +++ b/jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java @@ -40,7 +40,7 @@ import com.sun.nio.sctp.IllegalUnbindException; import com.sun.nio.sctp.SctpChannel; import com.sun.nio.sctp.SctpServerChannel; import com.sun.nio.sctp.SctpSocketOption; -import com.sun.nio.sctp.SctpStandardSocketOption; +import com.sun.nio.sctp.SctpStandardSocketOptions; /** * An implementation of SctpServerChannel @@ -386,7 +386,7 @@ public class SctpServerChannelImpl extends SctpServerChannel private static Set> defaultOptions() { HashSet> set = new HashSet>(1); - set.add(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS); + set.add(SctpStandardSocketOptions.SCTP_INIT_MAXSTREAMS); return Collections.unmodifiableSet(set); } } diff --git a/jdk/src/solaris/classes/sun/nio/fs/LinuxWatchService.java b/jdk/src/solaris/classes/sun/nio/fs/LinuxWatchService.java index 5726fe317ab..ec5535ed4ac 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/LinuxWatchService.java +++ b/jdk/src/solaris/classes/sun/nio/fs/LinuxWatchService.java @@ -210,15 +210,15 @@ class LinuxWatchService int mask = 0; for (WatchEvent.Kind event: events) { - if (event == StandardWatchEventKind.ENTRY_CREATE) { + if (event == StandardWatchEventKinds.ENTRY_CREATE) { mask |= IN_CREATE | IN_MOVED_TO; continue; } - if (event == StandardWatchEventKind.ENTRY_DELETE) { + if (event == StandardWatchEventKinds.ENTRY_DELETE) { mask |= IN_DELETE | IN_MOVED_FROM; continue; } - if (event == StandardWatchEventKind.ENTRY_MODIFY) { + if (event == StandardWatchEventKinds.ENTRY_MODIFY) { mask |= IN_MODIFY | IN_ATTRIB; continue; } @@ -378,17 +378,17 @@ class LinuxWatchService */ private WatchEvent.Kind maskToEventKind(int mask) { if ((mask & IN_MODIFY) > 0) - return StandardWatchEventKind.ENTRY_MODIFY; + return StandardWatchEventKinds.ENTRY_MODIFY; if ((mask & IN_ATTRIB) > 0) - return StandardWatchEventKind.ENTRY_MODIFY; + return StandardWatchEventKinds.ENTRY_MODIFY; if ((mask & IN_CREATE) > 0) - return StandardWatchEventKind.ENTRY_CREATE; + return StandardWatchEventKinds.ENTRY_CREATE; if ((mask & IN_MOVED_TO) > 0) - return StandardWatchEventKind.ENTRY_CREATE; + return StandardWatchEventKinds.ENTRY_CREATE; if ((mask & IN_DELETE) > 0) - return StandardWatchEventKind.ENTRY_DELETE; + return StandardWatchEventKinds.ENTRY_DELETE; if ((mask & IN_MOVED_FROM) > 0) - return StandardWatchEventKind.ENTRY_DELETE; + return StandardWatchEventKinds.ENTRY_DELETE; return null; } @@ -400,7 +400,7 @@ class LinuxWatchService if ((mask & IN_Q_OVERFLOW) > 0) { for (Map.Entry entry: wdToKey.entrySet()) { entry.getValue() - .signalEvent(StandardWatchEventKind.OVERFLOW, null); + .signalEvent(StandardWatchEventKinds.OVERFLOW, null); } return; } diff --git a/jdk/src/solaris/classes/sun/nio/fs/SolarisWatchService.java b/jdk/src/solaris/classes/sun/nio/fs/SolarisWatchService.java index fcf36316b19..76187ec4df7 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/SolarisWatchService.java +++ b/jdk/src/solaris/classes/sun/nio/fs/SolarisWatchService.java @@ -486,7 +486,7 @@ class SolarisWatchService void processDirectoryEvents(SolarisWatchKey key, int mask) { if ((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) { registerChildren(key.getDirectory(), key, - key.events().contains(StandardWatchEventKind.ENTRY_CREATE)); + key.events().contains(StandardWatchEventKinds.ENTRY_CREATE)); } } @@ -504,14 +504,14 @@ class SolarisWatchService // entry modified if (((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) && - events.contains(StandardWatchEventKind.ENTRY_MODIFY)) + events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) { - key.signalEvent(StandardWatchEventKind.ENTRY_MODIFY, node.name()); + key.signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, node.name()); } // entry removed if (((mask & (FILE_REMOVED)) != 0) && - events.contains(StandardWatchEventKind.ENTRY_DELETE)) + events.contains(StandardWatchEventKinds.ENTRY_DELETE)) { // Due to 6636438/6636412 we may get a remove event for cases // where a rmdir/unlink/rename is attempted but fails. Until @@ -527,7 +527,7 @@ class SolarisWatchService } catch (UnixException x) { } if (removed) - key.signalEvent(StandardWatchEventKind.ENTRY_DELETE, node.name()); + key.signalEvent(StandardWatchEventKinds.ENTRY_DELETE, node.name()); } return false; } @@ -547,7 +547,7 @@ class SolarisWatchService // if the ENTRY_MODIFY event is not enabled then we don't need // modification events for entries in the directory int events = FILE_NOFOLLOW; - if (parent.events().contains(StandardWatchEventKind.ENTRY_MODIFY)) + if (parent.events().contains(StandardWatchEventKinds.ENTRY_MODIFY)) events |= (FILE_MODIFIED | FILE_ATTRIB); DirectoryStream stream = null; @@ -567,7 +567,7 @@ class SolarisWatchService // send ENTRY_CREATE if enabled if (sendEvents) { - parent.signalEvent(StandardWatchEventKind.ENTRY_CREATE, name); + parent.signalEvent(StandardWatchEventKinds.ENTRY_CREATE, name); } // register it @@ -602,12 +602,12 @@ class SolarisWatchService // update events, rembering if ENTRY_MODIFY was previously // enabled or disabled. boolean wasModifyEnabled = key.events() - .contains(StandardWatchEventKind.ENTRY_MODIFY); + .contains(StandardWatchEventKinds.ENTRY_MODIFY); key.setEvents(events); // check if ENTRY_MODIFY has changed boolean isModifyEnabled = events - .contains(StandardWatchEventKind.ENTRY_MODIFY); + .contains(StandardWatchEventKinds.ENTRY_MODIFY); if (wasModifyEnabled == isModifyEnabled) { return; } diff --git a/jdk/src/windows/classes/sun/nio/fs/WindowsWatchService.java b/jdk/src/windows/classes/sun/nio/fs/WindowsWatchService.java index d5ad78fee9b..0e91726b541 100644 --- a/jdk/src/windows/classes/sun/nio/fs/WindowsWatchService.java +++ b/jdk/src/windows/classes/sun/nio/fs/WindowsWatchService.java @@ -464,15 +464,15 @@ class WindowsWatchService { switch (action) { case FILE_ACTION_MODIFIED : - return StandardWatchEventKind.ENTRY_MODIFY; + return StandardWatchEventKinds.ENTRY_MODIFY; case FILE_ACTION_ADDED : case FILE_ACTION_RENAMED_NEW_NAME : - return StandardWatchEventKind.ENTRY_CREATE; + return StandardWatchEventKinds.ENTRY_CREATE; case FILE_ACTION_REMOVED : case FILE_ACTION_RENAMED_OLD_NAME : - return StandardWatchEventKind.ENTRY_DELETE; + return StandardWatchEventKinds.ENTRY_DELETE; default : return null; // action not recognized @@ -548,7 +548,7 @@ class WindowsWatchService if (info.error() != 0) { // buffer overflow if (info.error() == ERROR_NOTIFY_ENUM_DIR) { - key.signalEvent(StandardWatchEventKind.OVERFLOW, null); + key.signalEvent(StandardWatchEventKinds.OVERFLOW, null); } else { // other error so cancel key implCancelKey(key); @@ -562,7 +562,7 @@ class WindowsWatchService processEvents(key, info.bytesTransferred()); } else { // insufficient buffer size - key.signalEvent(StandardWatchEventKind.OVERFLOW, null); + key.signalEvent(StandardWatchEventKinds.OVERFLOW, null); } // start read for next batch of changes diff --git a/jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java b/jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java index c9f94e0b26b..2ab14ac5b14 100644 --- a/jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java +++ b/jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java @@ -40,7 +40,7 @@ import com.sun.nio.sctp.SctpServerChannel; import com.sun.nio.sctp.SctpSocketOption; import java.security.AccessController; import sun.security.action.GetPropertyAction; -import static com.sun.nio.sctp.SctpStandardSocketOption.*; +import static com.sun.nio.sctp.SctpStandardSocketOptions.*; import static java.lang.System.out; public class SocketOptionTests { diff --git a/jdk/test/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java b/jdk/test/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java index 364cce286c0..fdb0e72ab4c 100644 --- a/jdk/test/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java +++ b/jdk/test/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java @@ -48,7 +48,7 @@ import com.sun.nio.sctp.SctpServerChannel; import com.sun.nio.sctp.SctpSocketOption; import java.security.AccessController; import sun.security.action.GetPropertyAction; -import static com.sun.nio.sctp.SctpStandardSocketOption.*; +import static com.sun.nio.sctp.SctpStandardSocketOptions.*; import static java.lang.System.out; public class SocketOptionTests { diff --git a/jdk/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java b/jdk/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java index dac878aab44..53f1384823b 100644 --- a/jdk/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java +++ b/jdk/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java @@ -29,7 +29,7 @@ import java.nio.channels.*; import java.net.*; -import static java.net.StandardSocketOption.*; +import static java.net.StandardSocketOptions.*; import java.io.IOException; import java.util.Set; import java.util.concurrent.ExecutionException; diff --git a/jdk/test/java/nio/channels/AsynchronousSocketChannel/Basic.java b/jdk/test/java/nio/channels/AsynchronousSocketChannel/Basic.java index 277ed40d4fe..7002dcd3814 100644 --- a/jdk/test/java/nio/channels/AsynchronousSocketChannel/Basic.java +++ b/jdk/test/java/nio/channels/AsynchronousSocketChannel/Basic.java @@ -29,7 +29,7 @@ import java.nio.ByteBuffer; import java.nio.channels.*; -import static java.net.StandardSocketOption.*; +import static java.net.StandardSocketOptions.*; import java.net.*; import java.util.Random; import java.util.concurrent.*; @@ -383,7 +383,7 @@ public class Basic { // write bytes and close connection SocketChannel sc = server.accept(); ByteBuffer src = genBuffer(); - sc.setOption(StandardSocketOption.SO_SNDBUF, src.remaining()); + sc.setOption(StandardSocketOptions.SO_SNDBUF, src.remaining()); while (src.hasRemaining()) sc.write(src); sc.close(); diff --git a/jdk/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java b/jdk/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java index 1f36a690ccf..8cab0956539 100644 --- a/jdk/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java +++ b/jdk/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java @@ -52,7 +52,7 @@ public class BasicMulticastTests { StandardProtocolFamily.INET : StandardProtocolFamily.INET6; DatagramChannel dc = DatagramChannel.open(family) - .setOption(StandardSocketOption.SO_REUSEADDR, true) + .setOption(StandardSocketOptions.SO_REUSEADDR, true) .bind(new InetSocketAddress(source, 0)); // check existing key is returned @@ -115,7 +115,7 @@ public class BasicMulticastTests { System.out.println("Exception Tests"); DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET) - .setOption(StandardSocketOption.SO_REUSEADDR, true) + .setOption(StandardSocketOptions.SO_REUSEADDR, true) .bind(new InetSocketAddress(0)); InetAddress group = InetAddress.getByName("225.4.5.6"); diff --git a/jdk/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java b/jdk/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java index d07df5947ad..8e6251d953c 100644 --- a/jdk/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java +++ b/jdk/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java @@ -59,7 +59,7 @@ public class MulticastSendReceiveTests { StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; DatagramChannel dc = DatagramChannel.open(family) .bind(new InetSocketAddress(local, 0)) - .setOption(StandardSocketOption.IP_MULTICAST_IF, nif); + .setOption(StandardSocketOptions.IP_MULTICAST_IF, nif); int id = rand.nextInt(); byte[] msg = Integer.toString(id).getBytes("UTF-8"); ByteBuffer buf = ByteBuffer.wrap(msg); @@ -146,7 +146,7 @@ public class MulticastSendReceiveTests { System.out.format("\nTest DatagramChannel to %s socket\n", family.name()); try (DatagramChannel dc = (family == UNSPEC) ? DatagramChannel.open() : DatagramChannel.open(family)) { - dc.setOption(StandardSocketOption.SO_REUSEADDR, true) + dc.setOption(StandardSocketOptions.SO_REUSEADDR, true) .bind(new InetSocketAddress(0)); // join group diff --git a/jdk/test/java/nio/channels/DatagramChannel/SocketOptionTests.java b/jdk/test/java/nio/channels/DatagramChannel/SocketOptionTests.java index 9e96cbfb97e..1f01bd971b2 100644 --- a/jdk/test/java/nio/channels/DatagramChannel/SocketOptionTests.java +++ b/jdk/test/java/nio/channels/DatagramChannel/SocketOptionTests.java @@ -31,7 +31,7 @@ import java.nio.channels.*; import java.net.*; import java.io.IOException; import java.util.*; -import static java.net.StandardSocketOption.*; +import static java.net.StandardSocketOptions.*; public class SocketOptionTests { diff --git a/jdk/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java b/jdk/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java index 77697248ca6..ee5ad4a3cff 100644 --- a/jdk/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java +++ b/jdk/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java @@ -32,7 +32,7 @@ import java.nio.channels.*; import java.net.*; import java.io.IOException; import java.util.*; -import static java.net.StandardSocketOption.*; +import static java.net.StandardSocketOptions.*; public class SocketOptionTests { diff --git a/jdk/test/java/nio/channels/SocketChannel/Shutdown.java b/jdk/test/java/nio/channels/SocketChannel/Shutdown.java index aa94ad4e95c..535972bde79 100644 --- a/jdk/test/java/nio/channels/SocketChannel/Shutdown.java +++ b/jdk/test/java/nio/channels/SocketChannel/Shutdown.java @@ -39,7 +39,7 @@ public class Shutdown { static void acceptAndReset(ServerSocketChannel ssc) throws IOException { SocketChannel peer = ssc.accept(); try { - peer.setOption(StandardSocketOption.SO_LINGER, 0); + peer.setOption(StandardSocketOptions.SO_LINGER, 0); peer.configureBlocking(false); peer.write(ByteBuffer.wrap(new byte[128*1024])); } finally { diff --git a/jdk/test/java/nio/channels/SocketChannel/SocketOptionTests.java b/jdk/test/java/nio/channels/SocketChannel/SocketOptionTests.java index 660b522d189..561d6c9cac0 100644 --- a/jdk/test/java/nio/channels/SocketChannel/SocketOptionTests.java +++ b/jdk/test/java/nio/channels/SocketChannel/SocketOptionTests.java @@ -32,7 +32,7 @@ import java.nio.channels.*; import java.net.*; import java.io.IOException; import java.util.*; -import static java.net.StandardSocketOption.*; +import static java.net.StandardSocketOptions.*; public class SocketOptionTests { diff --git a/jdk/test/java/nio/file/Files/CheckPermissions.java b/jdk/test/java/nio/file/Files/CheckPermissions.java index ba9536a7de6..6ac66be23fe 100644 --- a/jdk/test/java/nio/file/Files/CheckPermissions.java +++ b/jdk/test/java/nio/file/Files/CheckPermissions.java @@ -540,7 +540,7 @@ public class CheckPermissions { try (WatchService watcher = FileSystems.getDefault().newWatchService()) { prepare(); - testdir.register(watcher, StandardWatchEventKind.ENTRY_DELETE); + testdir.register(watcher, StandardWatchEventKinds.ENTRY_DELETE); assertCheckRead(testdir); } diff --git a/jdk/test/java/nio/file/WatchService/Basic.java b/jdk/test/java/nio/file/WatchService/Basic.java index a51b07f4588..fd1dee4b007 100644 --- a/jdk/test/java/nio/file/WatchService/Basic.java +++ b/jdk/test/java/nio/file/WatchService/Basic.java @@ -29,7 +29,7 @@ */ import java.nio.file.*; -import static java.nio.file.StandardWatchEventKind.*; +import static java.nio.file.StandardWatchEventKinds.*; import java.nio.file.attribute.*; import java.io.*; import java.util.*; @@ -100,7 +100,7 @@ public class Basic { // remove key and check that we got the ENTRY_CREATE event takeExpectedKey(watcher, myKey); checkExpectedEvent(myKey.pollEvents(), - StandardWatchEventKind.ENTRY_CREATE, name); + StandardWatchEventKinds.ENTRY_CREATE, name); System.out.println("reset key"); if (!myKey.reset()) @@ -121,7 +121,7 @@ public class Basic { Files.delete(file); takeExpectedKey(watcher, myKey); checkExpectedEvent(myKey.pollEvents(), - StandardWatchEventKind.ENTRY_DELETE, name); + StandardWatchEventKinds.ENTRY_DELETE, name); System.out.println("reset key"); if (!myKey.reset()) @@ -149,7 +149,7 @@ public class Basic { // remove key and check that we got the ENTRY_MODIFY event takeExpectedKey(watcher, myKey); checkExpectedEvent(myKey.pollEvents(), - StandardWatchEventKind.ENTRY_MODIFY, name); + StandardWatchEventKinds.ENTRY_MODIFY, name); System.out.println("OKAY"); // done @@ -424,7 +424,7 @@ public class Basic { // check that key1 got ENTRY_CREATE takeExpectedKey(watcher1, key1); checkExpectedEvent(key1.pollEvents(), - StandardWatchEventKind.ENTRY_CREATE, name2); + StandardWatchEventKinds.ENTRY_CREATE, name2); // check that key2 got zero events WatchKey key = watcher2.poll(); @@ -437,7 +437,7 @@ public class Basic { // check that key2 got ENTRY_DELETE takeExpectedKey(watcher2, key2); checkExpectedEvent(key2.pollEvents(), - StandardWatchEventKind.ENTRY_DELETE, name1); + StandardWatchEventKinds.ENTRY_DELETE, name1); // check that key1 got zero events key = watcher1.poll(); @@ -458,7 +458,7 @@ public class Basic { Files.createFile(file1); takeExpectedKey(watcher2, key2); checkExpectedEvent(key2.pollEvents(), - StandardWatchEventKind.ENTRY_CREATE, name1); + StandardWatchEventKinds.ENTRY_CREATE, name1); System.out.println("OKAY"); diff --git a/jdk/test/java/nio/file/WatchService/FileTreeModifier.java b/jdk/test/java/nio/file/WatchService/FileTreeModifier.java index dfca07f5aeb..d2bed2bd567 100644 --- a/jdk/test/java/nio/file/WatchService/FileTreeModifier.java +++ b/jdk/test/java/nio/file/WatchService/FileTreeModifier.java @@ -28,7 +28,7 @@ */ import java.nio.file.*; -import static java.nio.file.StandardWatchEventKind.*; +import static java.nio.file.StandardWatchEventKinds.*; import java.io.IOException; import java.io.OutputStream; import java.util.*; diff --git a/jdk/test/java/nio/file/WatchService/LotsOfEvents.java b/jdk/test/java/nio/file/WatchService/LotsOfEvents.java index 3512d0e7e6c..64ab7aca65d 100644 --- a/jdk/test/java/nio/file/WatchService/LotsOfEvents.java +++ b/jdk/test/java/nio/file/WatchService/LotsOfEvents.java @@ -29,7 +29,7 @@ */ import java.nio.file.*; -import static java.nio.file.StandardWatchEventKind.*; +import static java.nio.file.StandardWatchEventKinds.*; import java.io.IOException; import java.io.OutputStream; import java.util.*; diff --git a/jdk/test/java/nio/file/WatchService/SensitivityModifier.java b/jdk/test/java/nio/file/WatchService/SensitivityModifier.java index a6f5d8a4ae7..3027d8d4e09 100644 --- a/jdk/test/java/nio/file/WatchService/SensitivityModifier.java +++ b/jdk/test/java/nio/file/WatchService/SensitivityModifier.java @@ -29,7 +29,7 @@ */ import java.nio.file.*; -import static java.nio.file.StandardWatchEventKind.*; +import static java.nio.file.StandardWatchEventKinds.*; import java.io.OutputStream; import java.io.IOException; import java.util.Random; diff --git a/jdk/test/java/nio/file/WatchService/WithSecurityManager.java b/jdk/test/java/nio/file/WatchService/WithSecurityManager.java index 10dc0984680..ac2a70aa254 100644 --- a/jdk/test/java/nio/file/WatchService/WithSecurityManager.java +++ b/jdk/test/java/nio/file/WatchService/WithSecurityManager.java @@ -66,7 +66,7 @@ public class WithSecurityManager { // attempt to register directory try { dir.register(dir.getFileSystem().newWatchService(), - new WatchEvent.Kind[]{ StandardWatchEventKind.ENTRY_CREATE }, + new WatchEvent.Kind[]{ StandardWatchEventKinds.ENTRY_CREATE }, modifiers); if (expectedToFail) throw new RuntimeException("SecurityException not thrown"); From bce7c82aae8ff9561d129252e357fafb4fb2a7d3 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Mon, 9 May 2011 13:30:12 -0700 Subject: [PATCH 12/65] 7041950: Fix copyright Reviewed-by: okutsu --- .../sun/text/resources/BreakIteratorRules_th.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/jdk/src/share/classes/sun/text/resources/BreakIteratorRules_th.java b/jdk/src/share/classes/sun/text/resources/BreakIteratorRules_th.java index 2791687130d..82f015c879b 100644 --- a/jdk/src/share/classes/sun/text/resources/BreakIteratorRules_th.java +++ b/jdk/src/share/classes/sun/text/resources/BreakIteratorRules_th.java @@ -27,17 +27,7 @@ */ /* - * IBM Confidential - * OCO Source Materials - * - * IBM Java(tm)2 SDK, Standard Edition, v 1.2 - * - * (C) Copyright IBM Corp. 1999 - * - * The source code for this program is not published or otherwise divested of - * its trade secrets, irrespective of what has been deposited with the U.S. - * Copyright office. - * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. */ /* From 93cc60559cb366a9d4cf74d6740cf6aeb5a40aad Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Tue, 10 May 2011 07:00:36 +0800 Subject: [PATCH 13/65] 7041635: GSSContextSpi.java copyright notice error Reviewed-by: valeriep --- .../classes/sun/security/jgss/spi/GSSContextSpi.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/jdk/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java b/jdk/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java index 05cb07032ed..11aa6ba8029 100644 --- a/jdk/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java +++ b/jdk/src/share/classes/sun/security/jgss/spi/GSSContextSpi.java @@ -24,22 +24,10 @@ */ /* - * =========================================================================== - * IBM Confidential - * OCO Source Materials - * Licensed Materials - Property of IBM * * (C) Copyright IBM Corp. 1999 All Rights Reserved. - * - * The source code for this program is not published or otherwise divested of - * its trade secrets, irrespective of what has been deposited with the U.S. - * Copyright Office. - * * Copyright 1997 The Open Group Research Institute. All rights reserved. - * =========================================================================== - * */ - package sun.security.jgss.spi; import org.ietf.jgss.*; From 7edc69abf587269a4eeae68f6c328f55ee6dd378 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 9 May 2011 17:50:43 -0700 Subject: [PATCH 14/65] 7021645: Project Coin: Minor improvements to java.lang.Throwable Reviewed-by: mduigou --- .../share/classes/java/lang/Throwable.java | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/jdk/src/share/classes/java/lang/Throwable.java b/jdk/src/share/classes/java/lang/Throwable.java index d70fe26841a..a42306f41ae 100644 --- a/jdk/src/share/classes/java/lang/Throwable.java +++ b/jdk/src/share/classes/java/lang/Throwable.java @@ -336,7 +336,10 @@ public class Throwable implements Serializable { * Disabling of suppression should only occur in exceptional * circumstances where special requirements exist, such as a * virtual machine reusing exception objects under low-memory - * situations. + * situations. Circumstances where a given exception object is + * repeatedly caught and rethrown, such as to implement control + * flow between two sub-systems, is another situation where + * immutable throwable objects would be appropriate. * * @param message the detail message. * @param cause the cause. (A {@code null} value is permitted, @@ -423,6 +426,18 @@ public class Throwable implements Serializable { * {@link #Throwable(String,Throwable)}, this method cannot be called * even once. * + *

    An example of using this method on a legacy throwable type + * without other support for setting the cause is: + * + *

    +     * try {
    +     *     lowLevelOp();
    +     * } catch (LowLevelException le) {
    +     *     throw (HighLevelException)
    +     *           new HighLevelException().initCause(le); // Legacy constructor
    +     * }
    +     * 
    + * * @param cause the cause (which is saved for later retrieval by the * {@link #getCause()} method). (A {@code null} value is * permitted, and indicates that the cause is nonexistent or @@ -788,7 +803,8 @@ public class Throwable implements Serializable { * this throwable is permitted to return a zero-length array from this * method. Generally speaking, the array returned by this method will * contain one element for every frame that would be printed by - * {@code printStackTrace}. + * {@code printStackTrace}. Writes to the returned array do not + * affect future calls to this method. * * @return an array of stack trace elements representing the stack trace * pertaining to this throwable. @@ -971,8 +987,8 @@ public class Throwable implements Serializable { /** * Appends the specified exception to the exceptions that were * suppressed in order to deliver this exception. This method is - * typically called (automatically and implicitly) by the {@code - * try}-with-resources statement. + * thread-safe and typically called (automatically and implicitly) + * by the {@code try}-with-resources statement. * *

    The suppression behavior is enabled unless disabled * {@linkplain #Throwable(String, Throwable, boolean, boolean) via @@ -1043,7 +1059,9 @@ public class Throwable implements Serializable { * * If no exceptions were suppressed or {@linkplain * #Throwable(String, Throwable, boolean, boolean) suppression is - * disabled}, an empty array is returned. + * disabled}, an empty array is returned. This method is + * thread-safe. Writes to the returned array do not affect future + * calls to this method. * * @return an array containing all of the exceptions that were * suppressed to deliver this exception. From 42e49be1bc3807b8b3837d50f0cbf15130c3cb64 Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Mon, 9 May 2011 19:45:52 -0700 Subject: [PATCH 15/65] 7043301: assert(locals < caller->fp() || locals > (caller->fp() + 16)) failed: locals in save area Reviewed-by: kvn --- hotspot/src/cpu/sparc/vm/frame_sparc.cpp | 2 +- hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp | 5 ++++- hotspot/src/cpu/x86/vm/frame_x86.cpp | 2 +- hotspot/src/share/vm/runtime/frame.cpp | 4 ++-- hotspot/src/share/vm/runtime/frame.hpp | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp index d9dd1700ca2..10d4772ab78 100644 --- a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp @@ -811,7 +811,7 @@ intptr_t* frame::interpreter_frame_tos_at(jint offset) const { #ifdef ASSERT #define DESCRIBE_FP_OFFSET(name) \ - values.describe(-1, fp() + frame::name##_offset, #name) + values.describe(frame_no, fp() + frame::name##_offset, #name) void frame::describe_pd(FrameValues& values, int frame_no) { for (int w = 0; w < frame::register_save_words; w++) { diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp index ac855dad84a..ad53f8834a0 100644 --- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp @@ -1712,7 +1712,10 @@ int AbstractInterpreter::layout_activation(methodOop method, // frames so place the locals adjacent to the varargs area. locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; if (caller->is_interpreted_frame()) { - *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + rounded_cls) - STACK_BIAS; + int parm_words = method->size_of_parameters() * Interpreter::stackElementWords; + int delta = local_words - parm_words; + int computed_sp_adjustment = (delta > 0) ? round_to(delta, WordsPerLong) : 0; + *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + computed_sp_adjustment) - STACK_BIAS; } } if (TraceDeoptimization) { diff --git a/hotspot/src/cpu/x86/vm/frame_x86.cpp b/hotspot/src/cpu/x86/vm/frame_x86.cpp index cb5dded426a..21705957e50 100644 --- a/hotspot/src/cpu/x86/vm/frame_x86.cpp +++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp @@ -650,7 +650,7 @@ intptr_t* frame::interpreter_frame_tos_at(jint offset) const { #ifdef ASSERT #define DESCRIBE_FP_OFFSET(name) \ - values.describe(-1, fp() + frame::name##_offset, #name) + values.describe(frame_no, fp() + frame::name##_offset, #name) void frame::describe_pd(FrameValues& values, int frame_no) { if (is_interpreted_frame()) { diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp index efa97157f34..d5f490272b7 100644 --- a/hotspot/src/share/vm/runtime/frame.cpp +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -1423,7 +1423,7 @@ void FrameValues::describe(int owner, intptr_t* location, const char* descriptio } -bool FrameValues::validate() { +void FrameValues::validate() { _values.sort(compare); bool error = false; FrameValue prev; @@ -1446,7 +1446,7 @@ bool FrameValues::validate() { prev = fv; } } - return error; + assert(!error, "invalid layout"); } diff --git a/hotspot/src/share/vm/runtime/frame.hpp b/hotspot/src/share/vm/runtime/frame.hpp index 7e750d5f0bc..0273edf928f 100644 --- a/hotspot/src/share/vm/runtime/frame.hpp +++ b/hotspot/src/share/vm/runtime/frame.hpp @@ -511,7 +511,7 @@ class FrameValues { // Used by frame functions to describe locations. void describe(int owner, intptr_t* location, const char* description, int priority = 0); - bool validate(); + void validate(); void print(); }; From 8e56205189b517cbcb0320aa062b783f17f3788c Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Tue, 10 May 2011 00:33:21 -0700 Subject: [PATCH 16/65] 6883834: ParNew: assert(!_g->to()->is_in_reserved(obj),"Scanning field twice?") with LargeObjects tests Fixed process_chunk_boundaries(), used for parallel card scanning when using ParNew/CMS, so as to prevent double-scanning, or worse, non-scanning of imprecisely marked objects exceeding parallel chunk size. Made some sizing parameters for parallel card scanning diagnostic, disabled ParallelGCRetainPLAB, and elaborated and clarified some comments. Reviewed-by: stefank, johnc --- .../parNew/parCardTableModRefBS.cpp | 332 ++++++++++++------ .../parNew/parOopClosures.inline.hpp | 18 +- .../src/share/vm/memory/cardTableModRefBS.cpp | 18 +- .../src/share/vm/memory/cardTableModRefBS.hpp | 24 +- hotspot/src/share/vm/memory/cardTableRS.cpp | 10 +- hotspot/src/share/vm/memory/cardTableRS.hpp | 5 +- hotspot/src/share/vm/memory/space.cpp | 14 +- hotspot/src/share/vm/runtime/globals.hpp | 16 +- 8 files changed, 297 insertions(+), 140 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp index afc8797ea33..bfb4b5809d3 100644 --- a/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp +++ b/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp @@ -29,13 +29,14 @@ #include "memory/sharedHeap.hpp" #include "memory/space.inline.hpp" #include "memory/universe.hpp" +#include "oops/oop.inline.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/virtualspace.hpp" void CardTableModRefBS::non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr, - DirtyCardToOopClosure* dcto_cl, - ClearNoncleanCardWrapper* cl, + OopsInGenClosure* cl, + CardTableRS* ct, int n_threads) { assert(n_threads > 0, "Error: expected n_threads > 0"); assert((n_threads == 1 && ParallelGCThreads == 0) || @@ -49,14 +50,14 @@ void CardTableModRefBS::non_clean_card_iterate_parallel_work(Space* sp, MemRegio lowest_non_clean_base_chunk_index, lowest_non_clean_chunk_size); - int n_strides = n_threads * StridesPerThread; + int n_strides = n_threads * ParGCStridesPerThread; SequentialSubTasksDone* pst = sp->par_seq_tasks(); pst->set_n_threads(n_threads); pst->set_n_tasks(n_strides); int stride = 0; while (!pst->is_task_claimed(/* reference */ stride)) { - process_stride(sp, mr, stride, n_strides, dcto_cl, cl, + process_stride(sp, mr, stride, n_strides, cl, ct, lowest_non_clean, lowest_non_clean_base_chunk_index, lowest_non_clean_chunk_size); @@ -79,13 +80,13 @@ CardTableModRefBS:: process_stride(Space* sp, MemRegion used, jint stride, int n_strides, - DirtyCardToOopClosure* dcto_cl, - ClearNoncleanCardWrapper* cl, + OopsInGenClosure* cl, + CardTableRS* ct, jbyte** lowest_non_clean, uintptr_t lowest_non_clean_base_chunk_index, size_t lowest_non_clean_chunk_size) { - // We don't have to go downwards here; it wouldn't help anyway, - // because of parallelism. + // We go from higher to lower addresses here; it wouldn't help that much + // because of the strided parallelism pattern used here. // Find the first card address of the first chunk in the stride that is // at least "bottom" of the used region. @@ -98,25 +99,35 @@ process_stride(Space* sp, if ((uintptr_t)stride >= start_chunk_stride_num) { chunk_card_start = (jbyte*)(start_card + (stride - start_chunk_stride_num) * - CardsPerStrideChunk); + ParGCCardsPerStrideChunk); } else { // Go ahead to the next chunk group boundary, then to the requested stride. chunk_card_start = (jbyte*)(start_card + (n_strides - start_chunk_stride_num + stride) * - CardsPerStrideChunk); + ParGCCardsPerStrideChunk); } while (chunk_card_start < end_card) { - // We don't have to go downwards here; it wouldn't help anyway, - // because of parallelism. (We take care with "min_done"; see below.) + // Even though we go from lower to higher addresses below, the + // strided parallelism can interleave the actual processing of the + // dirty pages in various ways. For a specific chunk within this + // stride, we take care to avoid double scanning or missing a card + // by suitably initializing the "min_done" field in process_chunk_boundaries() + // below, together with the dirty region extension accomplished in + // DirtyCardToOopClosure::do_MemRegion(). + jbyte* chunk_card_end = chunk_card_start + ParGCCardsPerStrideChunk; // Invariant: chunk_mr should be fully contained within the "used" region. - jbyte* chunk_card_end = chunk_card_start + CardsPerStrideChunk; MemRegion chunk_mr = MemRegion(addr_for(chunk_card_start), chunk_card_end >= end_card ? used.end() : addr_for(chunk_card_end)); assert(chunk_mr.word_size() > 0, "[chunk_card_start > used_end)"); assert(used.contains(chunk_mr), "chunk_mr should be subset of used"); + DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision(), + cl->gen_boundary()); + ClearNoncleanCardWrapper clear_cl(dcto_cl, ct); + + // Process the chunk. process_chunk_boundaries(sp, dcto_cl, @@ -126,17 +137,30 @@ process_stride(Space* sp, lowest_non_clean_base_chunk_index, lowest_non_clean_chunk_size); + // We want the LNC array updates above in process_chunk_boundaries + // to be visible before any of the card table value changes as a + // result of the dirty card iteration below. + OrderAccess::storestore(); + // We do not call the non_clean_card_iterate_serial() version because - // we want to clear the cards, and the ClearNoncleanCardWrapper closure - // itself does the work of finding contiguous dirty ranges of cards to - // process (and clear). - cl->do_MemRegion(chunk_mr); + // we want to clear the cards: clear_cl here does the work of finding + // contiguous dirty ranges of cards to process and clear. + clear_cl.do_MemRegion(chunk_mr); // Find the next chunk of the stride. - chunk_card_start += CardsPerStrideChunk * n_strides; + chunk_card_start += ParGCCardsPerStrideChunk * n_strides; } } + +// If you want a talkative process_chunk_boundaries, +// then #define NOISY(x) x +#ifdef NOISY +#error "Encountered a global preprocessor flag, NOISY, which might clash with local definition to follow" +#else +#define NOISY(x) +#endif + void CardTableModRefBS:: process_chunk_boundaries(Space* sp, @@ -147,127 +171,233 @@ process_chunk_boundaries(Space* sp, uintptr_t lowest_non_clean_base_chunk_index, size_t lowest_non_clean_chunk_size) { - // We must worry about the chunk boundaries. + // We must worry about non-array objects that cross chunk boundaries, + // because such objects are both precisely and imprecisely marked: + // .. if the head of such an object is dirty, the entire object + // needs to be scanned, under the interpretation that this + // was an imprecise mark + // .. if the head of such an object is not dirty, we can assume + // precise marking and it's efficient to scan just the dirty + // cards. + // In either case, each scanned reference must be scanned precisely + // once so as to avoid cloning of a young referent. For efficiency, + // our closures depend on this property and do not protect against + // double scans. - // First, set our max_to_do: - HeapWord* max_to_do = NULL; uintptr_t cur_chunk_index = addr_to_chunk_index(chunk_mr.start()); cur_chunk_index = cur_chunk_index - lowest_non_clean_base_chunk_index; - if (chunk_mr.end() < used.end()) { - // This is not the last chunk in the used region. What is the last - // object? - HeapWord* last_block = sp->block_start(chunk_mr.end()); - assert(last_block <= chunk_mr.end(), "In case this property changes."); - if (last_block == chunk_mr.end() - || !sp->block_is_obj(last_block)) { - max_to_do = chunk_mr.end(); + NOISY(tty->print_cr("===========================================================================");) + NOISY(tty->print_cr(" process_chunk_boundary: Called with [" PTR_FORMAT "," PTR_FORMAT ")", + chunk_mr.start(), chunk_mr.end());) + // First, set "our" lowest_non_clean entry, which would be + // used by the thread scanning an adjoining left chunk with + // a non-array object straddling the mutual boundary. + // Find the object that spans our boundary, if one exists. + // first_block is the block possibly straddling our left boundary. + HeapWord* first_block = sp->block_start(chunk_mr.start()); + assert((chunk_mr.start() != used.start()) || (first_block == chunk_mr.start()), + "First chunk should always have a co-initial block"); + // Does the block straddle the chunk's left boundary, and is it + // a non-array object? + if (first_block < chunk_mr.start() // first block straddles left bdry + && sp->block_is_obj(first_block) // first block is an object + && !(oop(first_block)->is_objArray() // first block is not an array (arrays are precisely dirtied) + || oop(first_block)->is_typeArray())) { + // Find our least non-clean card, so that a left neighbour + // does not scan an object straddling the mutual boundary + // too far to the right, and attempt to scan a portion of + // that object twice. + jbyte* first_dirty_card = NULL; + jbyte* last_card_of_first_obj = + byte_for(first_block + sp->block_size(first_block) - 1); + jbyte* first_card_of_cur_chunk = byte_for(chunk_mr.start()); + jbyte* last_card_of_cur_chunk = byte_for(chunk_mr.last()); + jbyte* last_card_to_check = + (jbyte*) MIN2((intptr_t) last_card_of_cur_chunk, + (intptr_t) last_card_of_first_obj); + // Note that this does not need to go beyond our last card + // if our first object completely straddles this chunk. + for (jbyte* cur = first_card_of_cur_chunk; + cur <= last_card_to_check; cur++) { + jbyte val = *cur; + if (card_will_be_scanned(val)) { + first_dirty_card = cur; break; + } else { + assert(!card_may_have_been_dirty(val), "Error"); + } + } + if (first_dirty_card != NULL) { + NOISY(tty->print_cr(" LNC: Found a dirty card at " PTR_FORMAT " in current chunk", + first_dirty_card);) + assert(0 <= cur_chunk_index && cur_chunk_index < lowest_non_clean_chunk_size, + "Bounds error."); + assert(lowest_non_clean[cur_chunk_index] == NULL, + "Write exactly once : value should be stable hereafter for this round"); + lowest_non_clean[cur_chunk_index] = first_dirty_card; + } NOISY(else { + tty->print_cr(" LNC: Found no dirty card in current chunk; leaving LNC entry NULL"); + // In the future, we could have this thread look for a non-NULL value to copy from its + // right neighbour (up to the end of the first object). + if (last_card_of_cur_chunk < last_card_of_first_obj) { + tty->print_cr(" LNC: BEWARE!!! first obj straddles past right end of chunk:\n" + " might be efficient to get value from right neighbour?"); + } + }) + } else { + // In this case we can help our neighbour by just asking them + // to stop at our first card (even though it may not be dirty). + NOISY(tty->print_cr(" LNC: first block is not a non-array object; setting LNC to first card of current chunk");) + assert(lowest_non_clean[cur_chunk_index] == NULL, "Write once : value should be stable hereafter"); + jbyte* first_card_of_cur_chunk = byte_for(chunk_mr.start()); + lowest_non_clean[cur_chunk_index] = first_card_of_cur_chunk; + } + NOISY(tty->print_cr(" process_chunk_boundary: lowest_non_clean[" INTPTR_FORMAT "] = " PTR_FORMAT + " which corresponds to the heap address " PTR_FORMAT, + cur_chunk_index, lowest_non_clean[cur_chunk_index], + (lowest_non_clean[cur_chunk_index] != NULL) + ? addr_for(lowest_non_clean[cur_chunk_index]) + : NULL);) + NOISY(tty->print_cr("---------------------------------------------------------------------------");) + + // Next, set our own max_to_do, which will strictly/exclusively bound + // the highest address that we will scan past the right end of our chunk. + HeapWord* max_to_do = NULL; + if (chunk_mr.end() < used.end()) { + // This is not the last chunk in the used region. + // What is our last block? We check the first block of + // the next (right) chunk rather than strictly check our last block + // because it's potentially more efficient to do so. + HeapWord* const last_block = sp->block_start(chunk_mr.end()); + assert(last_block <= chunk_mr.end(), "In case this property changes."); + if ((last_block == chunk_mr.end()) // our last block does not straddle boundary + || !sp->block_is_obj(last_block) // last_block isn't an object + || oop(last_block)->is_objArray() // last_block is an array (precisely marked) + || oop(last_block)->is_typeArray()) { + max_to_do = chunk_mr.end(); + NOISY(tty->print_cr(" process_chunk_boundary: Last block on this card is not a non-array object;\n" + " max_to_do left at " PTR_FORMAT, max_to_do);) } else { - // It is an object and starts before the end of the current chunk. + assert(last_block < chunk_mr.end(), "Tautology"); + // It is a non-array object that straddles the right boundary of this chunk. // last_obj_card is the card corresponding to the start of the last object // in the chunk. Note that the last object may not start in // the chunk. - jbyte* last_obj_card = byte_for(last_block); - if (!card_may_have_been_dirty(*last_obj_card)) { - // The card containing the head is not dirty. Any marks in + jbyte* const last_obj_card = byte_for(last_block); + const jbyte val = *last_obj_card; + if (!card_will_be_scanned(val)) { + assert(!card_may_have_been_dirty(val), "Error"); + // The card containing the head is not dirty. Any marks on // subsequent cards still in this chunk must have been made - // precisely; we can cap processing at the end. + // precisely; we can cap processing at the end of our chunk. max_to_do = chunk_mr.end(); + NOISY(tty->print_cr(" process_chunk_boundary: Head of last object on this card is not dirty;\n" + " max_to_do left at " PTR_FORMAT, + max_to_do);) } else { // The last object must be considered dirty, and extends onto the // following chunk. Look for a dirty card in that chunk that will // bound our processing. jbyte* limit_card = NULL; - size_t last_block_size = sp->block_size(last_block); - jbyte* last_card_of_last_obj = + const size_t last_block_size = sp->block_size(last_block); + jbyte* const last_card_of_last_obj = byte_for(last_block + last_block_size - 1); - jbyte* first_card_of_next_chunk = byte_for(chunk_mr.end()); + jbyte* const first_card_of_next_chunk = byte_for(chunk_mr.end()); // This search potentially goes a long distance looking - // for the next card that will be scanned. For example, - // an object that is an array of primitives will not - // have any cards covering regions interior to the array - // that will need to be scanned. The scan can be terminated - // at the last card of the next chunk. That would leave - // limit_card as NULL and would result in "max_to_do" - // being set with the LNC value or with the end - // of the last block. - jbyte* last_card_of_next_chunk = first_card_of_next_chunk + - CardsPerStrideChunk; - assert(byte_for(chunk_mr.end()) - byte_for(chunk_mr.start()) - == CardsPerStrideChunk, "last card of next chunk may be wrong"); - jbyte* last_card_to_check = (jbyte*) MIN2(last_card_of_last_obj, - last_card_of_next_chunk); + // for the next card that will be scanned, terminating + // at the end of the last_block, if no earlier dirty card + // is found. + assert(byte_for(chunk_mr.end()) - byte_for(chunk_mr.start()) == ParGCCardsPerStrideChunk, + "last card of next chunk may be wrong"); for (jbyte* cur = first_card_of_next_chunk; - cur <= last_card_to_check; cur++) { - if (card_will_be_scanned(*cur)) { + cur <= last_card_of_last_obj; cur++) { + const jbyte val = *cur; + if (card_will_be_scanned(val)) { + NOISY(tty->print_cr(" Found a non-clean card " PTR_FORMAT " with value 0x%x", + cur, (int)val);) limit_card = cur; break; + } else { + assert(!card_may_have_been_dirty(val), "Error: card can't be skipped"); } } - assert(0 <= cur_chunk_index+1 && - cur_chunk_index+1 < lowest_non_clean_chunk_size, - "Bounds error."); - // LNC for the next chunk - jbyte* lnc_card = lowest_non_clean[cur_chunk_index+1]; - if (limit_card == NULL) { - limit_card = lnc_card; - } if (limit_card != NULL) { - if (lnc_card != NULL) { - limit_card = (jbyte*)MIN2((intptr_t)limit_card, - (intptr_t)lnc_card); - } max_to_do = addr_for(limit_card); + assert(limit_card != NULL && max_to_do != NULL, "Error"); + NOISY(tty->print_cr(" process_chunk_boundary: Found a dirty card at " PTR_FORMAT + " max_to_do set at " PTR_FORMAT " which is before end of last block in chunk: " + PTR_FORMAT " + " PTR_FORMAT " = " PTR_FORMAT, + limit_card, max_to_do, last_block, last_block_size, (last_block+last_block_size));) } else { + // The following is a pessimistic value, because it's possible + // that a dirty card on a subsequent chunk has been cleared by + // the time we get to look at it; we'll correct for that further below, + // using the LNC array which records the least non-clean card + // before cards were cleared in a particular chunk. + limit_card = last_card_of_last_obj; max_to_do = last_block + last_block_size; + assert(limit_card != NULL && max_to_do != NULL, "Error"); + NOISY(tty->print_cr(" process_chunk_boundary: Found no dirty card before end of last block in chunk\n" + " Setting limit_card to " PTR_FORMAT + " and max_to_do " PTR_FORMAT " + " PTR_FORMAT " = " PTR_FORMAT, + limit_card, last_block, last_block_size, max_to_do);) } + assert(0 < cur_chunk_index+1 && cur_chunk_index+1 < lowest_non_clean_chunk_size, + "Bounds error."); + // It is possible that a dirty card for the last object may have been + // cleared before we had a chance to examine it. In that case, the value + // will have been logged in the LNC for that chunk. + // We need to examine as many chunks to the right as this object + // covers. + const uintptr_t last_chunk_index_to_check = addr_to_chunk_index(last_block + last_block_size - 1) + - lowest_non_clean_base_chunk_index; + DEBUG_ONLY(const uintptr_t last_chunk_index = addr_to_chunk_index(used.end()) + - lowest_non_clean_base_chunk_index;) + assert(last_chunk_index_to_check <= last_chunk_index, + err_msg("Out of bounds: last_chunk_index_to_check " INTPTR_FORMAT + " exceeds last_chunk_index " INTPTR_FORMAT, + last_chunk_index_to_check, last_chunk_index)); + for (uintptr_t lnc_index = cur_chunk_index + 1; + lnc_index <= last_chunk_index_to_check; + lnc_index++) { + jbyte* lnc_card = lowest_non_clean[lnc_index]; + if (lnc_card != NULL) { + // we can stop at the first non-NULL entry we find + if (lnc_card <= limit_card) { + NOISY(tty->print_cr(" process_chunk_boundary: LNC card " PTR_FORMAT " is lower than limit_card " PTR_FORMAT, + " max_to_do will be lowered to " PTR_FORMAT " from " PTR_FORMAT, + lnc_card, limit_card, addr_for(lnc_card), max_to_do);) + limit_card = lnc_card; + max_to_do = addr_for(limit_card); + assert(limit_card != NULL && max_to_do != NULL, "Error"); + } + // In any case, we break now + break; + } // else continue to look for a non-NULL entry if any + } + assert(limit_card != NULL && max_to_do != NULL, "Error"); } + assert(max_to_do != NULL, "OOPS 1 !"); } - assert(max_to_do != NULL, "OOPS!"); + assert(max_to_do != NULL, "OOPS 2!"); } else { max_to_do = used.end(); + NOISY(tty->print_cr(" process_chunk_boundary: Last chunk of this space;\n" + " max_to_do left at " PTR_FORMAT, + max_to_do);) } + assert(max_to_do != NULL, "OOPS 3!"); // Now we can set the closure we're using so it doesn't to beyond // max_to_do. dcto_cl->set_min_done(max_to_do); #ifndef PRODUCT dcto_cl->set_last_bottom(max_to_do); #endif - - // Now we set *our" lowest_non_clean entry. - // Find the object that spans our boundary, if one exists. - // Nothing to do on the first chunk. - if (chunk_mr.start() > used.start()) { - // first_block is the block possibly spanning the chunk start - HeapWord* first_block = sp->block_start(chunk_mr.start()); - // Does the block span the start of the chunk and is it - // an object? - if (first_block < chunk_mr.start() && - sp->block_is_obj(first_block)) { - jbyte* first_dirty_card = NULL; - jbyte* last_card_of_first_obj = - byte_for(first_block + sp->block_size(first_block) - 1); - jbyte* first_card_of_cur_chunk = byte_for(chunk_mr.start()); - jbyte* last_card_of_cur_chunk = byte_for(chunk_mr.last()); - jbyte* last_card_to_check = - (jbyte*) MIN2((intptr_t) last_card_of_cur_chunk, - (intptr_t) last_card_of_first_obj); - for (jbyte* cur = first_card_of_cur_chunk; - cur <= last_card_to_check; cur++) { - if (card_will_be_scanned(*cur)) { - first_dirty_card = cur; break; - } - } - if (first_dirty_card != NULL) { - assert(0 <= cur_chunk_index && - cur_chunk_index < lowest_non_clean_chunk_size, - "Bounds error."); - lowest_non_clean[cur_chunk_index] = first_dirty_card; - } - } - } + NOISY(tty->print_cr("===========================================================================\n");) } +#undef NOISY + void CardTableModRefBS:: get_LNC_array_for_space(Space* sp, @@ -283,8 +413,8 @@ get_LNC_array_for_space(Space* sp, // LNC array for the covered region. Any later expansion can't affect // the used_at_save_marks region. // (I observed a bug in which the first thread to execute this would - // resize, and then it would cause "expand_and_allocates" that would - // Increase the number of chunks in the covered region. Then a second + // resize, and then it would cause "expand_and_allocate" that would + // increase the number of chunks in the covered region. Then a second // thread would come and execute this, see that the size didn't match, // and free and allocate again. So the first thread would be using a // freed "_lowest_non_clean" array.) diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp b/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp index c9c50b3f6a3..00177bb75d2 100644 --- a/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp @@ -77,7 +77,23 @@ inline void ParScanClosure::do_oop_work(T* p, if (!oopDesc::is_null(heap_oop)) { oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); if ((HeapWord*)obj < _boundary) { - assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?"); +#ifndef PRODUCT + if (_g->to()->is_in_reserved(obj)) { + tty->print_cr("Scanning field (" PTR_FORMAT ") twice?", p); + GenCollectedHeap* gch = (GenCollectedHeap*)Universe::heap(); + Space* sp = gch->space_containing(p); + oop obj = oop(sp->block_start(p)); + assert((HeapWord*)obj < (HeapWord*)p, "Error"); + tty->print_cr("Object: " PTR_FORMAT, obj); + tty->print_cr("-------"); + obj->print(); + tty->print_cr("-----"); + tty->print_cr("Heap:"); + tty->print_cr("-----"); + gch->print(); + ShouldNotReachHere(); + } +#endif // OK, we need to ensure that it is copied. // We read the klass and mark in this order, so that we can reliably // get the size of the object: if the mark we read is not a diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp index 356a77adeeb..c6645015dea 100644 --- a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp @@ -455,25 +455,29 @@ bool CardTableModRefBS::mark_card_deferred(size_t card_index) { return true; } - void CardTableModRefBS::non_clean_card_iterate_possibly_parallel(Space* sp, MemRegion mr, - DirtyCardToOopClosure* dcto_cl, - ClearNoncleanCardWrapper* cl) { + OopsInGenClosure* cl, + CardTableRS* ct) { if (!mr.is_empty()) { int n_threads = SharedHeap::heap()->n_par_threads(); if (n_threads > 0) { #ifndef SERIALGC - non_clean_card_iterate_parallel_work(sp, mr, dcto_cl, cl, n_threads); + non_clean_card_iterate_parallel_work(sp, mr, cl, ct, n_threads); #else // SERIALGC fatal("Parallel gc not supported here."); #endif // SERIALGC } else { // We do not call the non_clean_card_iterate_serial() version below because // we want to clear the cards (which non_clean_card_iterate_serial() does not - // do for us), and the ClearNoncleanCardWrapper closure itself does the work - // of finding contiguous dirty ranges of cards to process (and clear). - cl->do_MemRegion(mr); + // do for us): clear_cl here does the work of finding contiguous dirty ranges + // of cards to process and clear. + + DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision(), + cl->gen_boundary()); + ClearNoncleanCardWrapper clear_cl(dcto_cl, ct); + + clear_cl.do_MemRegion(mr); } } } diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp index a8d48515ce4..6dc44970726 100644 --- a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp @@ -173,18 +173,17 @@ class CardTableModRefBS: public ModRefBarrierSet { // A variant of the above that will operate in a parallel mode if // worker threads are available, and clear the dirty cards as it // processes them. - // ClearNoncleanCardWrapper cl must wrap the DirtyCardToOopClosure dcto_cl, - // which may itself be modified by the method. + // XXX ??? MemRegionClosure above vs OopsInGenClosure below XXX + // XXX some new_dcto_cl's take OopClosure's, plus as above there are + // some MemRegionClosures. Clean this up everywhere. XXX void non_clean_card_iterate_possibly_parallel(Space* sp, MemRegion mr, - DirtyCardToOopClosure* dcto_cl, - ClearNoncleanCardWrapper* cl); + OopsInGenClosure* cl, CardTableRS* ct); private: // Work method used to implement non_clean_card_iterate_possibly_parallel() // above in the parallel case. void non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr, - DirtyCardToOopClosure* dcto_cl, - ClearNoncleanCardWrapper* cl, + OopsInGenClosure* cl, CardTableRS* ct, int n_threads); protected: @@ -198,11 +197,6 @@ class CardTableModRefBS: public ModRefBarrierSet { // *** Support for parallel card scanning. - enum SomeConstantsForParallelism { - StridesPerThread = 2, - CardsPerStrideChunk = 256 - }; - // This is an array, one element per covered region of the card table. // Each entry is itself an array, with one element per chunk in the // covered region. Each entry of these arrays is the lowest non-clean @@ -235,7 +229,7 @@ class CardTableModRefBS: public ModRefBarrierSet { // covers the given address. uintptr_t addr_to_chunk_index(const void* addr) { uintptr_t card = (uintptr_t) byte_for(addr); - return card / CardsPerStrideChunk; + return card / ParGCCardsPerStrideChunk; } // Apply cl, which must either itself apply dcto_cl or be dcto_cl, @@ -243,8 +237,8 @@ class CardTableModRefBS: public ModRefBarrierSet { void process_stride(Space* sp, MemRegion used, jint stride, int n_strides, - DirtyCardToOopClosure* dcto_cl, - ClearNoncleanCardWrapper* cl, + OopsInGenClosure* cl, + CardTableRS* ct, jbyte** lowest_non_clean, uintptr_t lowest_non_clean_base_chunk_index, size_t lowest_non_clean_chunk_size); @@ -482,7 +476,7 @@ public: void verify_dirty_region(MemRegion mr) PRODUCT_RETURN; static size_t par_chunk_heapword_alignment() { - return CardsPerStrideChunk * card_size_in_words; + return ParGCCardsPerStrideChunk * card_size_in_words; } }; diff --git a/hotspot/src/share/vm/memory/cardTableRS.cpp b/hotspot/src/share/vm/memory/cardTableRS.cpp index 551ba7eb8a4..4b47da783a1 100644 --- a/hotspot/src/share/vm/memory/cardTableRS.cpp +++ b/hotspot/src/share/vm/memory/cardTableRS.cpp @@ -162,7 +162,7 @@ inline bool ClearNoncleanCardWrapper::clear_card_serial(jbyte* entry) { } ClearNoncleanCardWrapper::ClearNoncleanCardWrapper( - MemRegionClosure* dirty_card_closure, CardTableRS* ct) : + DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct) : _dirty_card_closure(dirty_card_closure), _ct(ct) { _is_par = (SharedHeap::heap()->n_par_threads() > 0); } @@ -246,10 +246,6 @@ void CardTableRS::write_ref_field_gc_par(void* field, oop new_val) { void CardTableRS::younger_refs_in_space_iterate(Space* sp, OopsInGenClosure* cl) { - DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, _ct_bs->precision(), - cl->gen_boundary()); - ClearNoncleanCardWrapper clear_cl(dcto_cl, this); - const MemRegion urasm = sp->used_region_at_save_marks(); #ifdef ASSERT // Convert the assertion check to a warning if we are running @@ -275,10 +271,10 @@ void CardTableRS::younger_refs_in_space_iterate(Space* sp, if (!urasm.equals(urasm2)) { warning("CMS+ParNew: Flickering used_region_at_save_marks()!!"); } + ShouldNotReachHere(); } #endif - _ct_bs->non_clean_card_iterate_possibly_parallel(sp, urasm, - dcto_cl, &clear_cl); + _ct_bs->non_clean_card_iterate_possibly_parallel(sp, urasm, cl, this); } void CardTableRS::clear_into_younger(Generation* gen, bool clear_perm) { diff --git a/hotspot/src/share/vm/memory/cardTableRS.hpp b/hotspot/src/share/vm/memory/cardTableRS.hpp index 72c5dacf83d..a15b85f74a5 100644 --- a/hotspot/src/share/vm/memory/cardTableRS.hpp +++ b/hotspot/src/share/vm/memory/cardTableRS.hpp @@ -31,7 +31,6 @@ class Space; class OopsInGenClosure; -class DirtyCardToOopClosure; // This kind of "GenRemSet" uses a card table both as shared data structure // for a mod ref barrier set and for the rem set information. @@ -167,7 +166,7 @@ public: }; class ClearNoncleanCardWrapper: public MemRegionClosure { - MemRegionClosure* _dirty_card_closure; + DirtyCardToOopClosure* _dirty_card_closure; CardTableRS* _ct; bool _is_par; private: @@ -179,7 +178,7 @@ private: inline bool clear_card_parallel(jbyte* entry); public: - ClearNoncleanCardWrapper(MemRegionClosure* dirty_card_closure, CardTableRS* ct); + ClearNoncleanCardWrapper(DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct); void do_MemRegion(MemRegion mr); }; diff --git a/hotspot/src/share/vm/memory/space.cpp b/hotspot/src/share/vm/memory/space.cpp index 1971332cd06..ff91ba30cfe 100644 --- a/hotspot/src/share/vm/memory/space.cpp +++ b/hotspot/src/share/vm/memory/space.cpp @@ -97,6 +97,14 @@ void DirtyCardToOopClosure::walk_mem_region(MemRegion mr, } } +// We get called with "mr" representing the dirty region +// that we want to process. Because of imprecise marking, +// we may need to extend the incoming "mr" to the right, +// and scan more. However, because we may already have +// scanned some of that extended region, we may need to +// trim its right-end back some so we do not scan what +// we (or another worker thread) may already have scanned +// or planning to scan. void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) { // Some collectors need to do special things whenever their dirty @@ -148,7 +156,7 @@ void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) { // e.g. the dirty card region is entirely in a now free object // -- something that could happen with a concurrent sweeper. bottom = MIN2(bottom, top); - mr = MemRegion(bottom, top); + MemRegion extended_mr = MemRegion(bottom, top); assert(bottom <= top && (_precision != CardTableModRefBS::ObjHeadPreciseArray || _min_done == NULL || @@ -156,8 +164,8 @@ void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) { "overlap!"); // Walk the region if it is not empty; otherwise there is nothing to do. - if (!mr.is_empty()) { - walk_mem_region(mr, bottom_obj, top); + if (!extended_mr.is_empty()) { + walk_mem_region(extended_mr, bottom_obj, top); } // An idempotent closure might be applied in any order, so we don't diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index 555c2273ae1..dc90959aa12 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -1460,8 +1460,10 @@ class CommandLineFlags { product(intx, ParallelGCBufferWastePct, 10, \ "wasted fraction of parallel allocation buffer.") \ \ - product(bool, ParallelGCRetainPLAB, true, \ - "Retain parallel allocation buffers across scavenges.") \ + diagnostic(bool, ParallelGCRetainPLAB, false, \ + "Retain parallel allocation buffers across scavenges; " \ + " -- disabled because this currently conflicts with " \ + " parallel card scanning under certain conditions ") \ \ product(intx, TargetPLABWastePct, 10, \ "target wasted space in last buffer as pct of overall allocation")\ @@ -1495,7 +1497,15 @@ class CommandLineFlags { product(uintx, ParGCDesiredObjsFromOverflowList, 20, \ "The desired number of objects to claim from the overflow list") \ \ - product(uintx, CMSParPromoteBlocksToClaim, 16, \ + diagnostic(intx, ParGCStridesPerThread, 2, \ + "The number of strides per worker thread that we divide up the " \ + "card table scanning work into") \ + \ + diagnostic(intx, ParGCCardsPerStrideChunk, 256, \ + "The number of cards in each chunk of the parallel chunks used " \ + "during card table scanning") \ + \ + product(uintx, CMSParPromoteBlocksToClaim, 16, \ "Number of blocks to attempt to claim when refilling CMS LAB for "\ "parallel GC.") \ \ From fee8d7fca4ae2f2c41a8a13817d83f9817fb6042 Mon Sep 17 00:00:00 2001 From: Christian Thalinger Date: Tue, 10 May 2011 00:45:03 -0700 Subject: [PATCH 17/65] 7042122: JSR 292: adjust various inline thresholds for JSR 292 API methods and method handle adapters Reviewed-by: jrose, never, kvn --- hotspot/src/share/vm/ci/ciMethodHandle.cpp | 2 +- hotspot/src/share/vm/ci/ciMethodHandle.hpp | 7 ++- hotspot/src/share/vm/opto/bytecodeInfo.cpp | 55 ++++++++++++------- hotspot/src/share/vm/opto/doCall.cpp | 29 +++++----- hotspot/src/share/vm/opto/parse.hpp | 4 +- .../src/share/vm/prims/methodHandleWalk.cpp | 46 +++++++++++----- .../src/share/vm/prims/methodHandleWalk.hpp | 3 +- 7 files changed, 95 insertions(+), 51 deletions(-) diff --git a/hotspot/src/share/vm/ci/ciMethodHandle.cpp b/hotspot/src/share/vm/ci/ciMethodHandle.cpp index ef57f800cab..27ceb1927cf 100644 --- a/hotspot/src/share/vm/ci/ciMethodHandle.cpp +++ b/hotspot/src/share/vm/ci/ciMethodHandle.cpp @@ -42,7 +42,7 @@ ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const { methodHandle callee(_callee->get_methodOop()); // We catch all exceptions here that could happen in the method // handle compiler and stop the VM. - MethodHandleCompiler mhc(h, callee, is_invokedynamic, THREAD); + MethodHandleCompiler mhc(h, callee, call_profile()->count(), is_invokedynamic, THREAD); if (!HAS_PENDING_EXCEPTION) { methodHandle m = mhc.compile(THREAD); if (!HAS_PENDING_EXCEPTION) { diff --git a/hotspot/src/share/vm/ci/ciMethodHandle.hpp b/hotspot/src/share/vm/ci/ciMethodHandle.hpp index 69c1379946c..788d374b5f9 100644 --- a/hotspot/src/share/vm/ci/ciMethodHandle.hpp +++ b/hotspot/src/share/vm/ci/ciMethodHandle.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_VM_CI_CIMETHODHANDLE_HPP #define SHARE_VM_CI_CIMETHODHANDLE_HPP +#include "ci/ciCallProfile.hpp" #include "ci/ciInstance.hpp" #include "prims/methodHandles.hpp" @@ -33,7 +34,8 @@ // The class represents a java.lang.invoke.MethodHandle object. class ciMethodHandle : public ciInstance { private: - ciMethod* _callee; + ciMethod* _callee; + ciCallProfile* _profile; // Return an adapter for this MethodHandle. ciMethod* get_adapter(bool is_invokedynamic) const; @@ -50,6 +52,9 @@ public: ciMethod* callee() const { return _callee; } void set_callee(ciMethod* m) { _callee = m; } + ciCallProfile* call_profile() const { return _profile; } + void set_call_profile(ciCallProfile* profile) { _profile = profile; } + // Return an adapter for a MethodHandle call. ciMethod* get_method_handle_adapter() const { return get_adapter(false); diff --git a/hotspot/src/share/vm/opto/bytecodeInfo.cpp b/hotspot/src/share/vm/opto/bytecodeInfo.cpp index 55844906bfa..d3caa640b27 100644 --- a/hotspot/src/share/vm/opto/bytecodeInfo.cpp +++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp @@ -89,7 +89,7 @@ static bool is_init_with_ea(ciMethod* callee_method, } // positive filter: should send be inlined? returns NULL, if yes, or rejection msg -const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const { +const char* InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const { // Allows targeted inlining if(callee_method->should_inline()) { *wci_result = *(WarmCallInfo::always_hot()); @@ -102,8 +102,7 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m // positive filter: should send be inlined? returns NULL (--> yes) // or rejection msg - int max_size = C->max_inline_size(); - int size = callee_method->code_size(); + int size = callee_method->code_size(); // Check for too many throws (and not too huge) if(callee_method->interpreter_throwout_count() > InlineThrowCount && @@ -120,18 +119,36 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m return NULL; // size and frequency are represented in a new way } + int default_max_inline_size = C->max_inline_size(); + int inline_small_code_size = InlineSmallCode / 4; + int max_inline_size = default_max_inline_size; + int call_site_count = method()->scale_count(profile.count()); int invoke_count = method()->interpreter_invocation_count(); - assert( invoke_count != 0, "Require invokation count greater than zero"); - int freq = call_site_count/invoke_count; + + // Bytecoded method handle adapters do not have interpreter + // profiling data but only made up MDO data. Get the counter from + // there. + if (caller_method->is_method_handle_adapter()) { + assert(method()->method_data_or_null(), "must have an MDO"); + ciMethodData* mdo = method()->method_data(); + ciProfileData* mha_profile = mdo->bci_to_data(caller_bci); + assert(mha_profile, "must exist"); + CounterData* cd = mha_profile->as_CounterData(); + invoke_count = cd->count(); + call_site_count = invoke_count; // use the same value + } + + assert(invoke_count != 0, "require invocation count greater than zero"); + int freq = call_site_count / invoke_count; // bump the max size if the call is frequent if ((freq >= InlineFrequencyRatio) || (call_site_count >= InlineFrequencyCount) || is_init_with_ea(callee_method, caller_method, C)) { - max_size = C->freq_inline_size(); - if (size <= max_size && TraceFrequencyInlining) { + max_inline_size = C->freq_inline_size(); + if (size <= max_inline_size && TraceFrequencyInlining) { CompileTask::print_inline_indent(inline_depth()); tty->print_cr("Inlined frequent method (freq=%d count=%d):", freq, call_site_count); CompileTask::print_inline_indent(inline_depth()); @@ -141,11 +158,11 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m } else { // Not hot. Check for medium-sized pre-existing nmethod at cold sites. if (callee_method->has_compiled_code() && - callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode/4) + callee_method->instructions_size(CompLevel_full_optimization) > inline_small_code_size) return "already compiled into a medium method"; } - if (size > max_size) { - if (max_size > C->max_inline_size()) + if (size > max_inline_size) { + if (max_inline_size > default_max_inline_size) return "hot method too big"; return "too big"; } @@ -154,7 +171,7 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m // negative filter: should send NOT be inlined? returns NULL, ok to inline, or rejection msg -const char* InlineTree::shouldNotInline(ciMethod *callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const { +const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const { // negative filter: should send NOT be inlined? returns NULL (--> inline) or rejection msg if (!UseOldInlining) { const char* fail = NULL; @@ -269,14 +286,13 @@ const char* InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_ } const char *msg = NULL; - if ((msg = shouldInline(callee_method, caller_method, caller_bci, - profile, wci_result)) != NULL) { + msg = should_inline(callee_method, caller_method, caller_bci, profile, wci_result); + if (msg != NULL) return msg; - } - if ((msg = shouldNotInline(callee_method, caller_method, - wci_result)) != NULL) { + + msg = should_not_inline(callee_method, caller_method, wci_result); + if (msg != NULL) return msg; - } if (InlineAccessors && callee_method->is_accessor()) { // accessor methods are not subject to any of the following limits. @@ -492,9 +508,8 @@ InlineTree *InlineTree::build_inline_tree_for_callee( ciMethod* callee_method, J new_depth_adjust -= 1; // don't count method handle calls from java.lang.invoke implem } if (new_depth_adjust != 0 && PrintInlining) { - stringStream nm1; caller_jvms->method()->print_name(&nm1); - stringStream nm2; callee_method->print_name(&nm2); - tty->print_cr("discounting inlining depth from %s to %s", nm1.base(), nm2.base()); + CompileTask::print_inline_indent(inline_depth()); + tty->print_cr(" \\-> discounting inline depth"); } if (new_depth_adjust != 0 && C->log()) { int id1 = C->log()->identify(caller_jvms->method()); diff --git a/hotspot/src/share/vm/opto/doCall.cpp b/hotspot/src/share/vm/opto/doCall.cpp index de066e992a3..374d9b79db4 100644 --- a/hotspot/src/share/vm/opto/doCall.cpp +++ b/hotspot/src/share/vm/opto/doCall.cpp @@ -73,7 +73,8 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, // Note: When we get profiling during stage-1 compiles, we want to pull // from more specific profile data which pertains to this inlining. // Right now, ignore the information in jvms->caller(), and do method[bci]. - ciCallProfile profile = jvms->method()->call_profile_at_bci(jvms->bci()); + ciCallProfile profile = jvms->method()->call_profile_at_bci(jvms->bci()); + Bytecodes::Code bytecode = jvms->method()->java_code_at_bci(jvms->bci()); // See how many times this site has been invoked. int site_count = profile.count(); @@ -116,7 +117,7 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, // MethodHandle.invoke* are native methods which obviously don't // have bytecodes and so normal inlining fails. if (call_method->is_method_handle_invoke()) { - if (jvms->method()->java_code_at_bci(jvms->bci()) != Bytecodes::_invokedynamic) { + if (bytecode != Bytecodes::_invokedynamic) { GraphKit kit(jvms); Node* n = kit.argument(0); @@ -128,14 +129,15 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, // Set the actually called method to have access to the class // and signature in the MethodHandleCompiler. method_handle->set_callee(call_method); + method_handle->set_call_profile(&profile); // Get an adapter for the MethodHandle. ciMethod* target_method = method_handle->get_method_handle_adapter(); - CallGenerator* hit_cg = NULL; - if (target_method != NULL) - hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor); - if (hit_cg != NULL && hit_cg->is_inline()) - return hit_cg; + if (target_method != NULL) { + CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor); + if (hit_cg != NULL && hit_cg->is_inline()) + return hit_cg; + } } return CallGenerator::for_direct_call(call_method); @@ -151,15 +153,16 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, // Set the actually called method to have access to the class // and signature in the MethodHandleCompiler. method_handle->set_callee(call_method); + method_handle->set_call_profile(&profile); // Get an adapter for the MethodHandle. ciMethod* target_method = method_handle->get_invokedynamic_adapter(); - CallGenerator* hit_cg = NULL; - if (target_method != NULL) - hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor); - if (hit_cg != NULL && hit_cg->is_inline()) { - CallGenerator* miss_cg = CallGenerator::for_dynamic_call(call_method); - return CallGenerator::for_predicted_dynamic_call(method_handle, miss_cg, hit_cg, prof_factor); + if (target_method != NULL) { + CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor); + if (hit_cg != NULL && hit_cg->is_inline()) { + CallGenerator* miss_cg = CallGenerator::for_dynamic_call(call_method); + return CallGenerator::for_predicted_dynamic_call(method_handle, miss_cg, hit_cg, prof_factor); + } } // If something failed, generate a normal dynamic call. diff --git a/hotspot/src/share/vm/opto/parse.hpp b/hotspot/src/share/vm/opto/parse.hpp index 85416bb756c..9237e659886 100644 --- a/hotspot/src/share/vm/opto/parse.hpp +++ b/hotspot/src/share/vm/opto/parse.hpp @@ -68,8 +68,8 @@ protected: JVMState* caller_jvms, int caller_bci); const char* try_to_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result); - const char* shouldInline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const; - const char* shouldNotInline(ciMethod* callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const; + const char* should_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const; + const char* should_not_inline(ciMethod* callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const; void print_inlining(ciMethod *callee_method, int caller_bci, const char *failure_msg) const; InlineTree *caller_tree() const { return _caller_tree; } diff --git a/hotspot/src/share/vm/prims/methodHandleWalk.cpp b/hotspot/src/share/vm/prims/methodHandleWalk.cpp index 281934cba46..1962fddca58 100644 --- a/hotspot/src/share/vm/prims/methodHandleWalk.cpp +++ b/hotspot/src/share/vm/prims/methodHandleWalk.cpp @@ -616,9 +616,10 @@ int MethodHandleWalker::argument_count_slow() { // ----------------------------------------------------------------------------- // MethodHandleCompiler -MethodHandleCompiler::MethodHandleCompiler(Handle root, methodHandle callee, bool is_invokedynamic, TRAPS) +MethodHandleCompiler::MethodHandleCompiler(Handle root, methodHandle callee, int invoke_count, bool is_invokedynamic, TRAPS) : MethodHandleWalker(root, is_invokedynamic, THREAD), _callee(callee), + _invoke_count(invoke_count), _thread(THREAD), _bytecode(THREAD, 50), _constants(THREAD, 10), @@ -1182,7 +1183,7 @@ constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const { methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const { - methodHandle nullHandle; + methodHandle empty; // Create a method that holds the generated bytecode. invokedynamic // has no receiver, normal MH calls do. int flags_bits; @@ -1191,13 +1192,16 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const { else flags_bits = (/*JVM_MH_INVOKE_BITS |*/ JVM_ACC_PUBLIC | JVM_ACC_FINAL | JVM_ACC_SYNTHETIC); - methodOop m_oop = oopFactory::new_method(bytecode_length(), - accessFlags_from(flags_bits), - 0, 0, 0, oopDesc::IsSafeConc, CHECK_(nullHandle)); - methodHandle m(THREAD, m_oop); - m_oop = NULL; // oop not GC safe + // Create a new method + methodHandle m; + { + methodOop m_oop = oopFactory::new_method(bytecode_length(), + accessFlags_from(flags_bits), + 0, 0, 0, oopDesc::IsSafeConc, CHECK_(empty)); + m = methodHandle(THREAD, m_oop); + } - constantPoolHandle cpool = get_constant_pool(CHECK_(nullHandle)); + constantPoolHandle cpool = get_constant_pool(CHECK_(empty)); m->set_constants(cpool()); m->set_name_index(_name_index); @@ -1212,16 +1216,32 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const { typeArrayHandle exception_handlers(THREAD, Universe::the_empty_int_array()); m->set_exception_table(exception_handlers()); - // Set the carry bit of the invocation counter to force inlining of - // the adapter. + // Set the invocation counter's count to the invoke count of the + // original call site. InvocationCounter* ic = m->invocation_counter(); - ic->set_carry_flag(); + ic->set(InvocationCounter::wait_for_compile, _invoke_count); // Rewrite the method and set up the constant pool cache. - objArrayOop m_array = oopFactory::new_system_objArray(1, CHECK_(nullHandle)); + objArrayOop m_array = oopFactory::new_system_objArray(1, CHECK_(empty)); objArrayHandle methods(THREAD, m_array); methods->obj_at_put(0, m()); - Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(nullHandle)); // Use fake class. + Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(empty)); // Use fake class. + + // Create a new MDO + { + methodDataOop mdo = oopFactory::new_methodData(m, CHECK_(empty)); + assert(m->method_data() == NULL, "there should not be an MDO yet"); + m->set_method_data(mdo); + + // Iterate over all profile data and set the count of the counter + // data entries to the original call site counter. + for (ProfileData* pd = mdo->first_data(); mdo->is_valid(pd); pd = mdo->next_data(pd)) { + if (pd->is_CounterData()) { + CounterData* cd = pd->as_CounterData(); + cd->set_count(_invoke_count); + } + } + } #ifndef PRODUCT if (TraceMethodHandles) { diff --git a/hotspot/src/share/vm/prims/methodHandleWalk.hpp b/hotspot/src/share/vm/prims/methodHandleWalk.hpp index 0136d9e3241..6fb2e986ee6 100644 --- a/hotspot/src/share/vm/prims/methodHandleWalk.hpp +++ b/hotspot/src/share/vm/prims/methodHandleWalk.hpp @@ -247,6 +247,7 @@ public: class MethodHandleCompiler : public MethodHandleWalker { private: methodHandle _callee; + int _invoke_count; // count the original call site has been executed KlassHandle _rklass; // Return type for casting. BasicType _rtype; KlassHandle _target_klass; @@ -416,7 +417,7 @@ private: methodHandle get_method_oop(TRAPS) const; public: - MethodHandleCompiler(Handle root, methodHandle call_method, bool for_invokedynamic, TRAPS); + MethodHandleCompiler(Handle root, methodHandle call_method, int invoke_count, bool for_invokedynamic, TRAPS); // Compile the given MH chain into bytecode. methodHandle compile(TRAPS); From 8afd78fe5938aed60dac797dc3d4b84ef1a98d64 Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Tue, 10 May 2011 17:06:26 +0400 Subject: [PATCH 18/65] 7034619: Scrollable Tabs don't appear with JDK7 Synth based LaF, different from Java 5/6 Reviewed-by: alexp --- .../javax/swing/plaf/synth/SynthTabbedPaneUI.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java b/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java index 84a773ab300..0686967412d 100644 --- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java +++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java @@ -488,6 +488,18 @@ public class SynthTabbedPaneUI extends BasicTabbedPaneUI paintContentBorder(tabContentContext, g, tabPlacement, selectedIndex); } + protected void paintTabArea(Graphics g, int tabPlacement, + int selectedIndex) { + // This can be invoked from ScrollabeTabPanel + Insets insets = tabPane.getInsets(); + int x = insets.left; + int y = insets.top; + int width = tabPane.getWidth() - insets.left - insets.right; + int height = tabPane.getHeight() - insets.top - insets.bottom; + + paintTabArea(tabAreaContext, g, tabPlacement, selectedIndex, + new Rectangle(x, y, width, height)); + } private void paintTabArea(SynthContext ss, Graphics g, int tabPlacement, int selectedIndex, From ba52c9f91ea6b5db19fc430b56600bfe93ec5570 Mon Sep 17 00:00:00 2001 From: Oleg Pekhovskiy Date: Tue, 10 May 2011 17:56:12 +0400 Subject: [PATCH 19/65] 7035053: java/awt/event/MouseWheelEvent/DisabledComponent/DisabledComponent.java fails against jdk7 b134 Reviewed-by: art, denis, ant, dcherepanov --- jdk/src/windows/native/sun/windows/awt_Choice.cpp | 6 ++++++ jdk/src/windows/native/sun/windows/awt_Component.cpp | 1 - jdk/src/windows/native/sun/windows/awt_Frame.cpp | 11 ----------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/jdk/src/windows/native/sun/windows/awt_Choice.cpp b/jdk/src/windows/native/sun/windows/awt_Choice.cpp index dfd9a6de6b3..0d014ac56f9 100644 --- a/jdk/src/windows/native/sun/windows/awt_Choice.cpp +++ b/jdk/src/windows/native/sun/windows/awt_Choice.cpp @@ -396,6 +396,12 @@ LRESULT CALLBACK AwtChoice::ListWindowProc(HWND hwnd, UINT message, DASSERT(::IsWindow(hwnd)); + // This branch is required for the proper work of AwtComponent::GetComponent() method + // while hovering drop-down list + if (message == WmAwtIsComponent) { + return (LRESULT)TRUE; + } + switch (message) { case WM_LBUTTONDOWN: { DWORD curPos = ::GetMessagePos(); diff --git a/jdk/src/windows/native/sun/windows/awt_Component.cpp b/jdk/src/windows/native/sun/windows/awt_Component.cpp index 024915fcffc..d4fe9e8766f 100644 --- a/jdk/src/windows/native/sun/windows/awt_Component.cpp +++ b/jdk/src/windows/native/sun/windows/awt_Component.cpp @@ -364,7 +364,6 @@ AwtComponent* AwtComponent::GetComponentImpl(HWND hWnd) { AwtComponent *component = (AwtComponent *)::GetWindowLongPtr(hWnd, GWLP_USERDATA); DASSERT(!component || !IsBadReadPtr(component, sizeof(AwtComponent)) ); - DASSERT(!component || component->GetHWnd() == hWnd ); return component; } diff --git a/jdk/src/windows/native/sun/windows/awt_Frame.cpp b/jdk/src/windows/native/sun/windows/awt_Frame.cpp index 6662d6d54c2..76811dfec03 100644 --- a/jdk/src/windows/native/sun/windows/awt_Frame.cpp +++ b/jdk/src/windows/native/sun/windows/awt_Frame.cpp @@ -344,17 +344,6 @@ LRESULT AwtFrame::ProxyWindowProc(UINT message, WPARAM wParam, LPARAM lParam, Ms SetImeTargetComponent(NULL); } break; - // TODO: when a Choice's list is dropped down and we're scrolling in - // the list WM_MOUSEWHEEL messages come to the poxy, not to the list. Why? - case WM_MOUSEWHEEL: - focusOwner = AwtComponent::GetComponent(sm_focusOwner); - if (focusOwner != NULL && - focusOwner != this) // avoid recursive calls - { - retValue = focusOwner->WindowProc(message, wParam, lParam); - mr = mrConsume; - } - break; case WM_SETFOCUS: if (sm_inSynthesizeFocus) break; // pass it up the WindowProc chain From cd2f47b0fd7759d91264b96fbe95712829cf9f8f Mon Sep 17 00:00:00 2001 From: Anthony Petrov Date: Tue, 10 May 2011 18:28:05 +0400 Subject: [PATCH 20/65] 7041387: Introduce new boolean system property java.awt.smartInvalidate The behavior introduced with 6852592 is now enabled by the new system property only Reviewed-by: dcherepanov --- jdk/src/share/classes/java/awt/Component.java | 11 +++--- jdk/src/share/classes/java/awt/Container.java | 34 ++++++++++++++----- .../awt/Component/Revalidate/Revalidate.java | 2 +- .../InvalidateMustRespectValidateRoots.java | 2 +- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/jdk/src/share/classes/java/awt/Component.java b/jdk/src/share/classes/java/awt/Component.java index f577887abe6..2225633c0db 100644 --- a/jdk/src/share/classes/java/awt/Component.java +++ b/jdk/src/share/classes/java/awt/Component.java @@ -2887,11 +2887,12 @@ public abstract class Component implements ImageObserver, MenuContainer, /** * Invalidates this component and its ancestors. *

    - * All the ancestors of this component up to the nearest validate root are - * marked invalid also. If there is no a validate root container for this - * component, all of its ancestors up to the root of the hierarchy are - * marked invalid as well. Marking a container invalid indicates - * that the container needs to be laid out. + * By default, all the ancestors of the component up to the top-most + * container of the hierarchy are marked invalid. If the {@code + * java.awt.smartInvalidate} system property is set to {@code true}, + * invalidation stops on the nearest validate root of this component. + * Marking a container invalid indicates that the container needs to + * be laid out. *

    * This method is called automatically when any layout-related information * changes (e.g. setting the bounds of the component, or adding the diff --git a/jdk/src/share/classes/java/awt/Container.java b/jdk/src/share/classes/java/awt/Container.java index 88f0865b187..c59aa90bc5f 100644 --- a/jdk/src/share/classes/java/awt/Container.java +++ b/jdk/src/share/classes/java/awt/Container.java @@ -41,6 +41,8 @@ import java.io.ObjectStreamField; import java.io.PrintStream; import java.io.PrintWriter; +import java.security.AccessController; + import java.util.Arrays; import java.util.EventListener; import java.util.HashSet; @@ -60,6 +62,8 @@ import sun.awt.dnd.SunDropTargetEvent; import sun.java2d.pipe.Region; +import sun.security.action.GetBooleanAction; + /** * A generic Abstract Window Toolkit(AWT) container object is a component * that can contain other AWT components. @@ -1506,12 +1510,18 @@ public class Container extends Component { * Layout-related changes, such as bounds of the validate root descendants, * do not affect the layout of the validate root parent. This peculiarity * enables the {@code invalidate()} method to stop invalidating the - * component hierarchy when the method encounters a validate root. + * component hierarchy when the method encounters a validate root. However, + * to preserve backward compatibility this new optimized behavior is + * enabled only when the {@code java.awt.smartInvalidate} system property + * value is set to {@code true}. *

    - * If a component hierarchy contains validate roots, the {@code validate()} - * method must be invoked on the validate root of a previously invalidated - * component, rather than on the top-level container (such as a {@code - * Frame} object) to restore the validity of the hierarchy later. + * If a component hierarchy contains validate roots and the new optimized + * {@code invalidate()} behavior is enabled, the {@code validate()} method + * must be invoked on the validate root of a previously invalidated + * component to restore the validity of the hierarchy later. Otherwise, + * calling the {@code validate()} method on the top-level container (such + * as a {@code Frame} object) should be used to restore the validity of the + * component hierarchy. *

    * The {@code Window} class and the {@code Applet} class are the validate * roots in AWT. Swing introduces more validate roots. @@ -1527,13 +1537,20 @@ public class Container extends Component { return false; } + private static final boolean isJavaAwtSmartInvalidate; + static { + // Don't lazy-read because every app uses invalidate() + isJavaAwtSmartInvalidate = AccessController.doPrivileged( + new GetBooleanAction("java.awt.smartInvalidate")); + } + /** * Invalidates the parent of the container unless the container * is a validate root. */ @Override void invalidateParent() { - if (!isValidateRoot()) { + if (!isJavaAwtSmartInvalidate || !isValidateRoot()) { super.invalidateParent(); } } @@ -1572,9 +1589,8 @@ public class Container extends Component { * automatically. Note that the ancestors of the container may be * invalidated also (see {@link Component#invalidate} for details.) * Therefore, to restore the validity of the hierarchy, the {@code - * validate()} method should be invoked on a validate root of an - * invalidated component, or on the top-most container if the hierarchy - * does not contain validate roots. + * validate()} method should be invoked on the top-most invalid + * container of the hierarchy. *

    * Validating the container may be a quite time-consuming operation. For * performance reasons a developer may postpone the validation of the diff --git a/jdk/test/java/awt/Component/Revalidate/Revalidate.java b/jdk/test/java/awt/Component/Revalidate/Revalidate.java index 670c374f87d..386e5394c6f 100644 --- a/jdk/test/java/awt/Component/Revalidate/Revalidate.java +++ b/jdk/test/java/awt/Component/Revalidate/Revalidate.java @@ -26,7 +26,7 @@ @bug 7036669 @summary Test Component.revalidate() method @author anthony.petrov@oracle.com: area=awt.component - @run main Revalidate + @run main/othervm -Djava.awt.smartInvalidate=true Revalidate */ import java.awt.*; diff --git a/jdk/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java b/jdk/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java index 9fd38aa51ec..0986ebd7976 100644 --- a/jdk/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java +++ b/jdk/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java @@ -26,7 +26,7 @@ @bug 6852592 @summary invalidate() must stop when it encounters a validate root @author anthony.petrov@sun.com - @run main InvalidateMustRespectValidateRoots + @run main/othervm -Djava.awt.smartInvalidate=true InvalidateMustRespectValidateRoots */ import javax.swing.*; From 165868a3f6039a85921431bc0af48d3e734bf1dc Mon Sep 17 00:00:00 2001 From: Mike Duigou Date: Tue, 10 May 2011 10:16:37 -0700 Subject: [PATCH 21/65] 7043513: Update test for StandardCharsets Reviewed-by: alanb --- .../Standard.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) rename jdk/test/java/nio/charset/{StandardCharset => StandardCharsets}/Standard.java (82%) diff --git a/jdk/test/java/nio/charset/StandardCharset/Standard.java b/jdk/test/java/nio/charset/StandardCharsets/Standard.java similarity index 82% rename from jdk/test/java/nio/charset/StandardCharset/Standard.java rename to jdk/test/java/nio/charset/StandardCharsets/Standard.java index cae083ed539..66d1b112a1b 100644 --- a/jdk/test/java/nio/charset/StandardCharset/Standard.java +++ b/jdk/test/java/nio/charset/StandardCharsets/Standard.java @@ -44,25 +44,25 @@ public class Standard { "UTF-16BE", "UTF-16LE", "UTF-16" }; public static void realMain(String[] args) { - check(StandardCharset.US_ASCII instanceof Charset); - check(StandardCharset.ISO_8859_1 instanceof Charset); - check(StandardCharset.UTF_8 instanceof Charset); - check(StandardCharset.UTF_16BE instanceof Charset); - check(StandardCharset.UTF_16LE instanceof Charset); - check(StandardCharset.UTF_16 instanceof Charset); + check(StandardCharsets.US_ASCII instanceof Charset); + check(StandardCharsets.ISO_8859_1 instanceof Charset); + check(StandardCharsets.UTF_8 instanceof Charset); + check(StandardCharsets.UTF_16BE instanceof Charset); + check(StandardCharsets.UTF_16LE instanceof Charset); + check(StandardCharsets.UTF_16 instanceof Charset); - check("US-ASCII".equals(StandardCharset.US_ASCII.name())); - check("ISO-8859-1".equals(StandardCharset.ISO_8859_1.name())); - check("UTF-8".equals(StandardCharset.UTF_8.name())); - check("UTF-16BE".equals(StandardCharset.UTF_16BE.name())); - check("UTF-16LE".equals(StandardCharset.UTF_16LE.name())); - check("UTF-16".equals(StandardCharset.UTF_16.name())); + check("US-ASCII".equals(StandardCharsets.US_ASCII.name())); + check("ISO-8859-1".equals(StandardCharsets.ISO_8859_1.name())); + check("UTF-8".equals(StandardCharsets.UTF_8.name())); + check("UTF-16BE".equals(StandardCharsets.UTF_16BE.name())); + check("UTF-16LE".equals(StandardCharsets.UTF_16LE.name())); + check("UTF-16".equals(StandardCharsets.UTF_16.name())); Set charsets = new HashSet<>(); - Field standardCharsetFields[] = StandardCharset.class.getFields(); + Field standardCharsetFields[] = StandardCharsets.class.getFields(); for(Field charsetField : standardCharsetFields) { - check(StandardCharset.class == charsetField.getDeclaringClass()); + check(StandardCharsets.class == charsetField.getDeclaringClass()); check(Modifier.isFinal(charsetField.getModifiers())); check(Modifier.isStatic(charsetField.getModifiers())); check(Modifier.isPublic(charsetField.getModifiers())); From f3d675ecef8efc21165a6eef76fac408c7ff56fe Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Tue, 10 May 2011 14:41:18 -0400 Subject: [PATCH 22/65] 7043443: address missed reason initialization in BatchUpdateException Reviewed-by: alanb --- jdk/src/share/classes/java/sql/BatchUpdateException.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jdk/src/share/classes/java/sql/BatchUpdateException.java b/jdk/src/share/classes/java/sql/BatchUpdateException.java index 4fa6241169f..8405da2dac7 100644 --- a/jdk/src/share/classes/java/sql/BatchUpdateException.java +++ b/jdk/src/share/classes/java/sql/BatchUpdateException.java @@ -89,7 +89,7 @@ public class BatchUpdateException extends SQLException { * The cause is not initialized, and may subsequently be * initialized by a call to the * {@link Throwable#initCause(java.lang.Throwable)} method. The vendor code - * is intialized to 0. + * is initialized to 0. *

    * * @param reason a description of the exception @@ -188,7 +188,7 @@ public class BatchUpdateException extends SQLException { * @since 1.6 */ public BatchUpdateException(Throwable cause) { - this(null, null, 0, null, cause); + this((cause == null ? null : cause.toString()), null, 0, null, cause); } /** @@ -214,7 +214,7 @@ public class BatchUpdateException extends SQLException { * @since 1.6 */ public BatchUpdateException(int []updateCounts , Throwable cause) { - this(null, null, 0, updateCounts, cause); + this((cause == null ? null : cause.toString()), null, 0, updateCounts, cause); } /** From 953f34d325dd0b4ffe7c2766819165a61b3e4505 Mon Sep 17 00:00:00 2001 From: Igor Veresov Date: Tue, 10 May 2011 12:26:10 -0700 Subject: [PATCH 23/65] 7043564: compile warning and copyright fixes Fixed the warning, also fixed copyrights in a bunch of files. Reviewed-by: johnc, kvn --- hotspot/src/os/linux/vm/os_linux.cpp | 2 +- .../vm/runtime/advancedThresholdPolicy.cpp | 25 ++++++++++++++++--- .../vm/runtime/advancedThresholdPolicy.hpp | 25 ++++++++++++++++--- .../vm/runtime/simpleThresholdPolicy.cpp | 2 +- .../vm/runtime/simpleThresholdPolicy.hpp | 2 +- 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp index f00e426b881..791f62ecb7b 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -2850,7 +2850,7 @@ bool os::Linux::hugetlbfs_sanity_check(bool warn, size_t page_size) { char chars[257]; long x = 0; if (fgets(chars, sizeof(chars), fp)) { - if (sscanf(chars, "%lx-%*lx", &x) == 1 + if (sscanf(chars, "%lx-%*x", &x) == 1 && x == (long)p) { if (strstr (chars, "hugepage")) { result = true; diff --git a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp index e195ff4854d..b761e51a511 100644 --- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp +++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp @@ -1,7 +1,26 @@ /* -* Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved. -* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. -*/ + * Copyright (c) 2010, 2011, 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. + * + */ #include "precompiled.hpp" #include "runtime/advancedThresholdPolicy.hpp" diff --git a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp index 1cab763e034..c1ebd0dc4e6 100644 --- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp +++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp @@ -1,7 +1,26 @@ /* -* Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved. -* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. -*/ + * Copyright (c) 2010, 2011, 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. + * + */ #ifndef SHARE_VM_RUNTIME_ADVANCEDTHRESHOLDPOLICY_HPP #define SHARE_VM_RUNTIME_ADVANCEDTHRESHOLDPOLICY_HPP diff --git a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp index 4d23753521f..fae5312622a 100644 --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2011, 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 diff --git a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp index 527c3f60b59..bc392c84b58 100644 --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2011, 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 From 70dfd21e87376ccfa1aaf2a2f98375f93c3ba28d Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Tue, 10 May 2011 12:57:21 -0700 Subject: [PATCH 24/65] 7043552: regression after 7042327 Generate Opaque2 node only during first unroll. Reviewed-by: never --- hotspot/src/share/vm/opto/loopTransform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/opto/loopTransform.cpp b/hotspot/src/share/vm/opto/loopTransform.cpp index a5d40ba2597..f720b79ce63 100644 --- a/hotspot/src/share/vm/opto/loopTransform.cpp +++ b/hotspot/src/share/vm/opto/loopTransform.cpp @@ -1230,7 +1230,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad set_ctrl(new_limit, C->root()); } else { // Limit is not constant. - { + if (loop_head->unrolled_count() == 1) { // only for first unroll // Separate limit by Opaque node in case it is an incremented // variable from previous loop to avoid using pre-incremented // value which could increase register pressure. From de0dac8ac810fb29ac4488ead3940f37d33f414e Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 10 May 2011 15:59:01 -0700 Subject: [PATCH 25/65] 7040717: Test case for 6522514 was not included in bug fix Reviewed-by: prr --- .../awt/geom/Arc2D/SerializationTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 jdk/test/java/awt/geom/Arc2D/SerializationTest.java diff --git a/jdk/test/java/awt/geom/Arc2D/SerializationTest.java b/jdk/test/java/awt/geom/Arc2D/SerializationTest.java new file mode 100644 index 00000000000..1d90a43e5a1 --- /dev/null +++ b/jdk/test/java/awt/geom/Arc2D/SerializationTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2011, 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. + */ + +/** + * @test + * @bug 7040717 6522514 + * @summary Verifies that subclasses of Arc2D can be serialized. + * @run main SerializationTest + */ + +import java.awt.geom.Rectangle2D; +import java.awt.geom.Arc2D; +import java.io.Serializable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class SerializationTest { + static boolean failed; + public static void main(String args[]) { + test(new Arc()); + test(new ArcF()); + test(new ArcD()); + if (failed) throw new RuntimeException("some tests failed"); + } + + public static void test(Object a) { + try { + File objectbin = new File("object.bin"); + FileOutputStream fos = new FileOutputStream(objectbin); + ObjectOutputStream out = new ObjectOutputStream(fos); + out.writeObject(a); + fos.close(); + FileInputStream fis = new FileInputStream(objectbin); + ObjectInputStream in = new ObjectInputStream(fis); + Object o = in.readObject(); + fis.close(); + System.err.println(o); + } catch (Throwable ex) { + ex.printStackTrace(); + failed = true; + } + } + + static class Arc extends Arc2D implements Serializable { + public Arc() { + super(Arc2D.OPEN); + } + + public Rectangle2D makeBounds(double x, double y, double w, double h) { + return new Rectangle2D.Double(x, y, w, h); + } + public double getX() { return 0; } + public double getY() { return 0; } + public double getWidth() { return 0; } + public double getHeight() { return 0; } + public double getAngleExtent() { return 0; } + public double getAngleStart() { return 0; } + public void setAngleExtent(double angExt) { } + public void setAngleStart(double angExt) { } + public void setFrame(double x, double y, double w, double h) {} + public void setArc(double x, double y, double w, double h, + double s, double e, int c) + { + } + public boolean isEmpty() { return false; }; + } + + static class ArcF extends Arc2D.Float implements Serializable { + public ArcF() { + } + } + + static class ArcD extends Arc2D.Double implements Serializable { + public ArcD() { + } + } +} From 2f96d5c458ebfb13f6284d967da107f7a1309e89 Mon Sep 17 00:00:00 2001 From: Yuka Kamiya Date: Wed, 11 May 2011 08:02:44 +0900 Subject: [PATCH 26/65] 7041232: IllegalArgumentException in sun.text.bidi.BidiBase.setLine starting from JDK 7 b64 Reviewed-by: okutsu --- .../share/classes/sun/text/bidi/BidiBase.java | 4 -- jdk/test/java/text/Bidi/Bug7041232.java | 48 +++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 jdk/test/java/text/Bidi/Bug7041232.java diff --git a/jdk/src/share/classes/sun/text/bidi/BidiBase.java b/jdk/src/share/classes/sun/text/bidi/BidiBase.java index 8be648e931c..8983ab6a668 100644 --- a/jdk/src/share/classes/sun/text/bidi/BidiBase.java +++ b/jdk/src/share/classes/sun/text/bidi/BidiBase.java @@ -2889,10 +2889,6 @@ public class BidiBase { verifyValidPara(); verifyRange(start, 0, limit); verifyRange(limit, 0, length+1); - if (getParagraphIndex(start) != getParagraphIndex(limit - 1)) { - /* the line crosses a paragraph boundary */ - throw new IllegalArgumentException(); - } return BidiLine.setLine(bidi, this, newBidi, newBidiBase, start, limit); } diff --git a/jdk/test/java/text/Bidi/Bug7041232.java b/jdk/test/java/text/Bidi/Bug7041232.java new file mode 100644 index 00000000000..f8e84e293c4 --- /dev/null +++ b/jdk/test/java/text/Bidi/Bug7041232.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* + * @test + * @bug 7041232 + * @summary verify that an unexpected exception isn't thrown for unnatural data to keep backward compatibility with JDK 6. + */ +import java.text.*; + +public class Bug7041232 { + + public static void main(String[] args) { + String UnicodeChars; + StringBuffer sb = new StringBuffer(); + + // Generates String which includes U+2028(line separator) and + // U+2029(paragraph separator) + for (int i = 0x2000; i < 0x2100; i++) { + sb.append((char)i); + } + UnicodeChars = sb.toString(); + + Bidi bidi = new Bidi(UnicodeChars, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + bidi.createLineBidi(0, UnicodeChars.length()); + } + +} From a3640362171a4bdd614c1e2480afd57c61f365a6 Mon Sep 17 00:00:00 2001 From: Kelly O'Hair Date: Tue, 10 May 2011 16:59:15 -0700 Subject: [PATCH 27/65] 7042773: Integrate JAXWS 2.2.4 update to JDK7 Reviewed-by: ramap --- jaxws/jaxws.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jaxws/jaxws.properties b/jaxws/jaxws.properties index cfae88e1bc1..a1544f4bb93 100644 --- a/jaxws/jaxws.properties +++ b/jaxws/jaxws.properties @@ -25,8 +25,8 @@ drops.master.copy.base=${drops.dir} -jaxws_src.bundle.name=jdk7-jaxws2_2_4-b01-2011_04_08.zip -jaxws_src.bundle.md5.checksum=9f35dd731c99ddb62db650aaf20e5bf4 +jaxws_src.bundle.name=jdk7-jaxws2_2_4-b02-2011_05_09.zip +jaxws_src.bundle.md5.checksum=0972a58090e8b2735d91a2f5d1ae1964 jaxws_src.master.bundle.dir=${drops.master.copy.base} jaxws_src.master.bundle.url.base=http://download.java.net/glassfish/components/jax-ws/openjdk/jdk7 From 249ae523240a87dc60871d1fae317e28f077673f Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Tue, 10 May 2011 17:44:14 -0700 Subject: [PATCH 28/65] 7043040: JSR292: JRuby bench/shootout/binarytrees.ruby-2.ruby SEGV: constantPoolKlass::oop_follow_contents Reviewed-by: kvn, ysr --- .../src/share/vm/oops/constantPoolKlass.cpp | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/hotspot/src/share/vm/oops/constantPoolKlass.cpp b/hotspot/src/share/vm/oops/constantPoolKlass.cpp index 627a2017ebe..a5ca5cdfc7f 100644 --- a/hotspot/src/share/vm/oops/constantPoolKlass.cpp +++ b/hotspot/src/share/vm/oops/constantPoolKlass.cpp @@ -285,10 +285,9 @@ int constantPoolKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { void constantPoolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_constantPool(), "should be constant pool"); constantPoolOop cp = (constantPoolOop) obj; - if (cp->tags() != NULL && - (!JavaObjectsInPerm || (EnableInvokeDynamic && cp->has_pseudo_string()))) { + if (cp->tags() != NULL) { for (int i = 1; i < cp->length(); ++i) { - if (cp->tag_at(i).is_string()) { + if (cp->is_pointer_entry(i)) { oop* base = cp->obj_at_addr_raw(i); if (PSScavenge::should_scavenge(base)) { pm->claim_or_forward_depth(base); @@ -342,6 +341,11 @@ void constantPoolKlass::oop_print_on(oop obj, outputStream* st) { anObj->print_value_on(st); st->print(" {0x%lx}", (address)anObj); break; + case JVM_CONSTANT_Object : + anObj = cp->object_at(index); + anObj->print_value_on(st); + st->print(" {0x%lx}", (address)anObj); + break; case JVM_CONSTANT_Integer : st->print("%d", cp->int_at(index)); break; @@ -432,23 +436,21 @@ void constantPoolKlass::oop_verify_on(oop obj, outputStream* st) { guarantee(cp->is_perm(), "should be in permspace"); if (!cp->partially_loaded()) { for (int i = 0; i< cp->length(); i++) { + constantTag tag = cp->tag_at(i); CPSlot entry = cp->slot_at(i); - if (cp->tag_at(i).is_klass()) { + if (tag.is_klass()) { if (entry.is_oop()) { guarantee(entry.get_oop()->is_perm(), "should be in permspace"); guarantee(entry.get_oop()->is_klass(), "should be klass"); } - } - if (cp->tag_at(i).is_unresolved_klass()) { + } else if (tag.is_unresolved_klass()) { if (entry.is_oop()) { guarantee(entry.get_oop()->is_perm(), "should be in permspace"); guarantee(entry.get_oop()->is_klass(), "should be klass"); } - } - if (cp->tag_at(i).is_symbol()) { + } else if (tag.is_symbol()) { guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count"); - } - if (cp->tag_at(i).is_unresolved_string()) { + } else if (tag.is_unresolved_string()) { if (entry.is_oop()) { guarantee(entry.get_oop()->is_perm(), "should be in permspace"); guarantee(entry.get_oop()->is_instance(), "should be instance"); @@ -456,8 +458,7 @@ void constantPoolKlass::oop_verify_on(oop obj, outputStream* st) { else { guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count"); } - } - if (cp->tag_at(i).is_string()) { + } else if (tag.is_string()) { if (!cp->has_pseudo_string()) { if (entry.is_oop()) { guarantee(!JavaObjectsInPerm || entry.get_oop()->is_perm(), @@ -467,8 +468,11 @@ void constantPoolKlass::oop_verify_on(oop obj, outputStream* st) { } else { // can be non-perm, can be non-instance (array) } + } else if (tag.is_object()) { + assert(entry.get_oop()->is_oop(), "should be some valid oop"); + } else { + assert(!cp->is_pointer_entry(i), "unhandled oop type in constantPoolKlass::verify_on"); } - // FIXME: verify JSR 292 tags JVM_CONSTANT_MethodHandle, etc. } guarantee(cp->tags()->is_perm(), "should be in permspace"); guarantee(cp->tags()->is_typeArray(), "should be type array"); From c0a3fc99bd7960fb5245fc364a549cbdfdf602d2 Mon Sep 17 00:00:00 2001 From: Andrei Dmitriev Date: Wed, 11 May 2011 15:00:25 +0400 Subject: [PATCH 29/65] 7042429: jdk 7 b140: crashes in awt.dll+0xb85fb] Java_sun_awt_Win32GraphicsEnvironment_isVistaOS+0xfdf Reviewed-by: bae, dcherepanov --- .../windows/native/sun/java2d/d3d/D3DPipelineManager.cpp | 8 ++++++++ jdk/src/windows/native/sun/windows/Devices.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/jdk/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp b/jdk/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp index a7da3f3b38c..212b912cc53 100644 --- a/jdk/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp +++ b/jdk/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp @@ -191,6 +191,14 @@ void D3DPipelineManager::NotifyAdapterEventListeners(UINT adapter, pMgr = D3DPipelineManager::GetInstance(); RETURN_IF_NULL(pMgr); hMon = pMgr->pd3d9->GetAdapterMonitor(adapter); + + /* + * If we don't have devices initialized yet, no sense to clear them. + */ + if (!Devices::GetInstance()){ + return; + } + gdiScreen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(hMon); JNU_CallStaticMethodByName(env, NULL, diff --git a/jdk/src/windows/native/sun/windows/Devices.h b/jdk/src/windows/native/sun/windows/Devices.h index bbd8a497aaf..14e9e7b7f0d 100644 --- a/jdk/src/windows/native/sun/windows/Devices.h +++ b/jdk/src/windows/native/sun/windows/Devices.h @@ -36,6 +36,7 @@ class AwtWin32GraphicsDevice; class Devices { public: +static Devices* GetInstance(); static BOOL UpdateInstance(JNIEnv *env); int GetNumDevices() { return numDevices; } AwtWin32GraphicsDevice* GetDeviceReference(int index, BOOL adjust = TRUE); @@ -59,7 +60,6 @@ friend class InstanceAccess; private: Devices(int numElements); void AddReference(); -static Devices* GetInstance(); AwtWin32GraphicsDevice** devices; int refCount; From b14549c78aada776d1913d2af88a1b4da88e698a Mon Sep 17 00:00:00 2001 From: Anthony Petrov Date: Wed, 11 May 2011 17:51:46 +0400 Subject: [PATCH 30/65] 7043455: Taking a screenshot may fail on X11 after 6903034 Backout 6903034 Reviewed-by: art, dcherepanov --- jdk/make/sun/xawt/mapfile-vers | 1 - .../classes/sun/awt/X11/XRobotPeer.java | 3 +- jdk/src/solaris/native/sun/awt/awt_Robot.c | 107 +----------------- 3 files changed, 4 insertions(+), 107 deletions(-) diff --git a/jdk/make/sun/xawt/mapfile-vers b/jdk/make/sun/xawt/mapfile-vers index 096bfe55326..8a12b69c314 100644 --- a/jdk/make/sun/xawt/mapfile-vers +++ b/jdk/make/sun/xawt/mapfile-vers @@ -158,7 +158,6 @@ SUNWprivate_1.1 { Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl; Java_sun_awt_X11_XRobotPeer_mouseWheelImpl; Java_sun_awt_X11_XRobotPeer_setup; - Java_sun_awt_X11_XRobotPeer__1dispose; Java_sun_awt_X11_XToolkit_getNumberOfButtonsImpl; Java_java_awt_Component_initIDs; Java_java_awt_Container_initIDs; diff --git a/jdk/src/solaris/classes/sun/awt/X11/XRobotPeer.java b/jdk/src/solaris/classes/sun/awt/X11/XRobotPeer.java index 1463f283f0b..a6acd8eb5f5 100644 --- a/jdk/src/solaris/classes/sun/awt/X11/XRobotPeer.java +++ b/jdk/src/solaris/classes/sun/awt/X11/XRobotPeer.java @@ -48,7 +48,7 @@ class XRobotPeer implements RobotPeer { } public void dispose() { - _dispose(); + // does nothing } public void mouseMove(int x, int y) { @@ -88,7 +88,6 @@ class XRobotPeer implements RobotPeer { } private static native synchronized void setup(int numberOfButtons, int[] buttonDownMasks); - private static native synchronized void _dispose(); private static native synchronized void mouseMoveImpl(X11GraphicsConfig xgc, int x, int y); private static native synchronized void mousePressImpl(int buttons); diff --git a/jdk/src/solaris/native/sun/awt/awt_Robot.c b/jdk/src/solaris/native/sun/awt/awt_Robot.c index 38ad2786366..c3384b2eb1a 100644 --- a/jdk/src/solaris/native/sun/awt/awt_Robot.c +++ b/jdk/src/solaris/native/sun/awt/awt_Robot.c @@ -48,28 +48,12 @@ #ifdef __linux__ #include #endif -#include extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs; static jint * masks; static jint num_buttons; -static unsigned int s_robotInstanceCounter = 0; - -static void* xcompositeLibHandle = NULL; -static Bool xcompositeExtAvailable = False; -static Bool xcompositeExtTested = False; - -typedef Status (*T_XCompositeQueryVersion)(Display *dpy, int *major_versionp, int *minor_versionp); -typedef Window (*T_XCompositeGetOverlayWindow)(Display *dpy, Window window); -typedef void (*T_XCompositeReleaseOverlayWindow)(Display *dpy, Window window); - -static T_XCompositeQueryVersion XCompositeQueryVersion = NULL; -static T_XCompositeGetOverlayWindow XCompositeGetOverlayWindow = NULL; -static T_XCompositeReleaseOverlayWindow XCompositeReleaseOverlayWindow = NULL; - - static int32_t isXTestAvailable() { int32_t major_opcode, first_event, first_error; int32_t event_basep, error_basep, majorp, minorp; @@ -210,80 +194,8 @@ Java_sun_awt_X11_XRobotPeer_setup (JNIEnv * env, jclass cls, jint numberOfButton } AWT_UNLOCK(); - - s_robotInstanceCounter++; } -JNIEXPORT void JNICALL -Java_sun_awt_X11_XRobotPeer__1dispose (JNIEnv * env, jclass cls) -{ - if (--s_robotInstanceCounter) { - return; - } - - // This is the last instance of the XRobotPeer being released - - if (xcompositeExtTested && xcompositeExtAvailable && xcompositeLibHandle) { - // The lib is loaded in IsXCompositeAvailable(). Unload under AWT_LOCK - // so that the shutdown function of the lib behaves correctly. - AWT_LOCK(); - dlclose(xcompositeLibHandle); - AWT_UNLOCK(); - } - - xcompositeExtTested = False; - xcompositeExtAvailable = False; - xcompositeLibHandle = NULL; -} - -/* - * Returns True only if XCOMPOSITE is of version 0.3 or higher. - * The functions that we need are available since that version. - * - * Must be invoked under AWT_LOCK. - * - * Leaves the library loaded if the version is correct. - */ -static Bool IsXCompositeAvailable() -{ - if (!xcompositeExtTested) { - int opcode, eventb, errorb; - - if (XQueryExtension(awt_display, "Composite", &opcode, &eventb, &errorb)) { - xcompositeLibHandle = dlopen("libXcomposite.so.1", RTLD_LAZY | RTLD_GLOBAL); -#ifndef __linux__ /* SOLARIS */ - if (xcompositeLibHandle == NULL) { - xcompositeLibHandle = dlopen("/usr/sfw/lib/libXcomposite.so.1", - RTLD_LAZY | RTLD_GLOBAL); - } -#endif - - if (xcompositeLibHandle) { - int major, minor; - XCompositeQueryVersion = (T_XCompositeQueryVersion)dlsym(xcompositeLibHandle, "XCompositeQueryVersion"); - - if (XCompositeQueryVersion && XCompositeQueryVersion(awt_display, &major, &minor)) { - if (major >= 0 && minor >= 3) { - XCompositeGetOverlayWindow = (T_XCompositeGetOverlayWindow)dlsym(xcompositeLibHandle, "XCompositeGetOverlayWindow"); - XCompositeReleaseOverlayWindow = (T_XCompositeReleaseOverlayWindow)dlsym(xcompositeLibHandle, "XCompositeReleaseOverlayWindow"); - - if (XCompositeGetOverlayWindow && XCompositeReleaseOverlayWindow) { - xcompositeExtAvailable = True; - } - } - } - - if (!xcompositeExtAvailable) { - dlclose(xcompositeLibHandle); - } /* else the lib is unloaded in _dispose() */ - } - } - - xcompositeExtTested = True; - } - - return xcompositeExtAvailable; -} JNIEXPORT void JNICALL Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, @@ -299,7 +211,7 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, jint *ary; /* Array of jints for sending pixel values back * to parent process. */ - Window window; + Window rootWindow; AwtGraphicsConfigDataPtr adata; DTRACE_PRINTLN6("RobotPeer: getRGBPixelsImpl(%lx, %d, %d, %d, %d, %x)", xgc, x, y, width, height, pixelArray); @@ -316,24 +228,14 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData); DASSERT(adata != NULL); - window = XRootWindow(awt_display, adata->awt_visInfo.screen); - - if (IsXCompositeAvailable()) { - // Use 'composite overlay window' instead of the root window. - // See 6903034 for details. - window = XCompositeGetOverlayWindow(awt_display, window); - } - - image = getWindowImage(awt_display, window, x, y, width, height); + rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen); + image = getWindowImage(awt_display, rootWindow, x, y, width, height); /* Array to use to crunch around the pixel values */ ary = (jint *) malloc(width * height * sizeof (jint)); if (ary == NULL) { JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError"); XDestroyImage(image); - if (IsXCompositeAvailable()) { - XCompositeReleaseOverlayWindow(awt_display, window); - } AWT_UNLOCK(); return; } @@ -354,9 +256,6 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, free(ary); XDestroyImage(image); - if (IsXCompositeAvailable()) { - XCompositeReleaseOverlayWindow(awt_display, window); - } AWT_UNLOCK(); } From 54cdd2715c143ebf8949ca4c2ab936b9dda879f4 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Wed, 11 May 2011 14:57:17 +0100 Subject: [PATCH 31/65] 7043425: (fc) ClosedByInterruptException thrown but interrupt status not set Reviewed-by: dholmes, chegar --- .../share/classes/sun/nio/ch/NativeThreadSet.java | 7 ++++++- .../channels/FileChannel/ClosedByInterrupt.java | 15 ++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/jdk/src/share/classes/sun/nio/ch/NativeThreadSet.java b/jdk/src/share/classes/sun/nio/ch/NativeThreadSet.java index b4ddbbb71ac..51eabbcaf23 100644 --- a/jdk/src/share/classes/sun/nio/ch/NativeThreadSet.java +++ b/jdk/src/share/classes/sun/nio/ch/NativeThreadSet.java @@ -96,11 +96,16 @@ class NativeThreadSet { break; } waitingToEmpty = true; + boolean interrupted = false; while (used > 0) { try { wait(); - } catch (InterruptedException ignore) { } + } catch (InterruptedException e) { + interrupted = true; + } } + if (interrupted) + Thread.currentThread().interrupt(); } } } diff --git a/jdk/test/java/nio/channels/FileChannel/ClosedByInterrupt.java b/jdk/test/java/nio/channels/FileChannel/ClosedByInterrupt.java index 24e2119d4cc..26c3d89789a 100644 --- a/jdk/test/java/nio/channels/FileChannel/ClosedByInterrupt.java +++ b/jdk/test/java/nio/channels/FileChannel/ClosedByInterrupt.java @@ -52,13 +52,16 @@ public class ClosedByInterrupt { fc.write(bb); } - // test with 1-8 concurrent threads - for (int i=1; i<=8; i++) { + // test with 1-16 concurrent threads + for (int i=1; i<=16; i++) { System.out.format("%d thread(s)%n", i); test(f, i); if (failed) break; } + + if (failed) + throw new RuntimeException("Test failed"); } /** @@ -132,12 +135,14 @@ public class ClosedByInterrupt { // give the interruptible thread a chance try { Thread.sleep(rand.nextInt(50)); - } catch (InterruptedException ignore) { } + } catch (InterruptedException e) { + unexpected(e); + } } } } catch (ClosedByInterruptException e) { if (interruptible) { - if (Thread.currentThread().isInterrupted()) { + if (Thread.interrupted()) { expected(e + " thrown and interrupt status set"); } else { unexpected(e + " thrown but interrupt status not set"); @@ -158,7 +163,7 @@ public class ClosedByInterrupt { } static void expected(Exception e) { - System.out.format("%s (not expected)%n", e); + System.out.format("%s (expected)%n", e); } static void expected(String msg) { From fa107ec80ea844a4fa809dedf6385f534c3274aa Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Wed, 11 May 2011 15:00:16 +0100 Subject: [PATCH 32/65] 7043788: (fs) PosixFileAttributes.owner() or group() throws NPE if owner/group not in passwd/group database Reviewed-by: chegar --- .../native/sun/nio/fs/UnixNativeDispatcher.c | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c b/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c index ba22f417556..f41409b051e 100644 --- a/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c +++ b/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c @@ -892,8 +892,9 @@ Java_sun_nio_fs_UnixNativeDispatcher_getpwuid(JNIEnv* env, jclass this, jint uid if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') { /* not found or error */ - if (errno != 0 && errno != ENOENT) - throwUnixException(env, errno); + if (errno == 0) + errno = ENOENT; + throwUnixException(env, errno); } else { jsize len = strlen(p->pw_name); result = (*env)->NewByteArray(env, len); @@ -941,14 +942,14 @@ Java_sun_nio_fs_UnixNativeDispatcher_getgrgid(JNIEnv* env, jclass this, jint gid retry = 0; if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') { /* not found or error */ - if (errno != 0 && errno != ENOENT) { - if (errno == ERANGE) { - /* insufficient buffer size so need larger buffer */ - buflen += ENT_BUF_SIZE; - retry = 1; - } else { - throwUnixException(env, errno); - } + if (errno == ERANGE) { + /* insufficient buffer size so need larger buffer */ + buflen += ENT_BUF_SIZE; + retry = 1; + } else { + if (errno == 0) + errno = ENOENT; + throwUnixException(env, errno); } } else { jsize len = strlen(g->gr_name); From dcf6e9a4c2e67170d2e790ae16eab937f53b3716 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Wed, 11 May 2011 08:54:16 -0700 Subject: [PATCH 33/65] 7043234: (fmt) java.util.Formatter links in javadoc to BigDecimal need to be fixed Fixed the doc miss Reviewed-by: alanb, emcmanus --- jdk/src/share/classes/java/util/Formatter.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jdk/src/share/classes/java/util/Formatter.java b/jdk/src/share/classes/java/util/Formatter.java index 5ca126ab31d..bcd36ea6120 100644 --- a/jdk/src/share/classes/java/util/Formatter.java +++ b/jdk/src/share/classes/java/util/Formatter.java @@ -826,7 +826,7 @@ import sun.misc.FormattedFloatingDecimal; * *

  • Float and Double * - *
  • BigDecimal + *
  • BigDecimal * * * @@ -1362,7 +1362,7 @@ import sun.misc.FormattedFloatingDecimal; * precision is not provided, then all of the digits as returned by {@link * Double#toHexString(double)} will be output. * - *

    BigDecimal + *

    BigDecimal * *

    The following conversions may be applied {@link java.math.BigDecimal * BigDecimal}. @@ -1372,7 +1372,7 @@ import sun.misc.FormattedFloatingDecimal; * {@code 'e'} * '\u0065' * Requires the output to be formatted using computerized scientific notation. The computerized scientific notation. The localization algorithm is applied. * *

    The formatting of the magnitude m depends upon its value. @@ -1427,11 +1427,11 @@ import sun.misc.FormattedFloatingDecimal; * *

    If m is greater than or equal to 10-4 but less * than 10precision then it is represented in decimal format. + * href="#bdecimal">decimal format. * *

    If m is less than 10-4 or greater than or equal to * 10precision, then it is represented in computerized scientific notation. + * href="#bscientific">computerized scientific notation. * *

    The total number of significant digits in m is equal to the * precision. If the precision is not specified, then the default value is @@ -1447,7 +1447,7 @@ import sun.misc.FormattedFloatingDecimal; * * {@code 'f'} * '\u0066' - * Requires the output to be formatted using decimal + * Requires the output to be formatted using decimal * format. The localization algorithm is * applied. * From 1d148aa7d31228df5119c8d32f89f330348660f0 Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Wed, 11 May 2011 08:59:46 -0700 Subject: [PATCH 34/65] 7043298: 4/4 fix for 7028172 causes "Label too long: ..." error message Use '-e' version of sed expressions. Clarify and fix comments Reviewed-by: never, acorn --- hotspot/make/solaris/makefiles/saproc.make | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/hotspot/make/solaris/makefiles/saproc.make b/hotspot/make/solaris/makefiles/saproc.make index a0ea9c75db9..027faaae6b4 100644 --- a/hotspot/make/solaris/makefiles/saproc.make +++ b/hotspot/make/solaris/makefiles/saproc.make @@ -57,24 +57,27 @@ SA_LFLAGS += -mt -xnolib -norunpath endif # The libproc Pstack_iter() interface changed in Nevada-B159. -# This logic needs to match +# Use 'uname -r -v' to determine the Solaris version as per +# Solaris Nevada team request. This logic needs to match: # agent/src/os/solaris/proc/saproc.cpp: set_has_newer_Pstack_iter(): # - skip SunOS 4 or older # - skip Solaris 10 or older -# - skip two digit Nevada builds -# - skip three digit Nevada builds thru 149 -# - skip Nevada builds 150-158 +# - skip two digit internal Nevada builds +# - skip three digit internal Nevada builds thru 149 +# - skip internal Nevada builds 150-158 +# - if not skipped, print define for Nevada-B159 or later SOLARIS_11_B159_OR_LATER := \ $(shell uname -r -v \ - | sed -n ' \ - /^[0-3]\. /b \ - /^5\.[0-9] /b \ - /^5\.10 /b \ - / snv_[0-9][0-9]$/b \ - / snv_[01][0-4][0-9]$/b \ - / snv_15[0-8]$/b \ - s/.*/-DSOLARIS_11_B159_OR_LATER/p \ - ') + | sed -n \ + -e '/^[0-4]\. /b' \ + -e '/^5\.[0-9] /b' \ + -e '/^5\.10 /b' \ + -e '/ snv_[0-9][0-9]$/b' \ + -e '/ snv_[01][0-4][0-9]$/b' \ + -e '/ snv_15[0-8]$/b' \ + -e 's/.*/-DSOLARIS_11_B159_OR_LATER/' \ + -e 'p' \ + ) # Uncomment the following to simulate building on Nevada-B159 or later # when actually building on Nevada-B158 or earlier: From c40a4d68789f37518582367b2898f7b46230d0a8 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Wed, 11 May 2011 15:47:12 -0700 Subject: [PATCH 35/65] 7043891: CMS: assert(_whole_heap.contains(p)) failed: out of bounds access to card marking array Fixed assertion checking code that was attempting to translate addresses past end of space for card-table slot. Also elaborated some assertion checking messages. Reviewed-by: iveresov, jmasa, tonyp --- .../parNew/parCardTableModRefBS.cpp | 2 +- .../src/share/vm/memory/blockOffsetTable.cpp | 17 +++++++++++++++-- .../src/share/vm/memory/cardTableModRefBS.hpp | 12 +++++++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp index bfb4b5809d3..1585c5d8387 100644 --- a/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp +++ b/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp @@ -351,7 +351,7 @@ process_chunk_boundaries(Space* sp, // covers. const uintptr_t last_chunk_index_to_check = addr_to_chunk_index(last_block + last_block_size - 1) - lowest_non_clean_base_chunk_index; - DEBUG_ONLY(const uintptr_t last_chunk_index = addr_to_chunk_index(used.end()) + DEBUG_ONLY(const uintptr_t last_chunk_index = addr_to_chunk_index(used.last()) - lowest_non_clean_base_chunk_index;) assert(last_chunk_index_to_check <= last_chunk_index, err_msg("Out of bounds: last_chunk_index_to_check " INTPTR_FORMAT diff --git a/hotspot/src/share/vm/memory/blockOffsetTable.cpp b/hotspot/src/share/vm/memory/blockOffsetTable.cpp index 7caafeeb977..210f744a1f0 100644 --- a/hotspot/src/share/vm/memory/blockOffsetTable.cpp +++ b/hotspot/src/share/vm/memory/blockOffsetTable.cpp @@ -541,20 +541,33 @@ HeapWord* BlockOffsetArrayNonContigSpace::block_start_unsafe( // to go back by. size_t n_cards_back = entry_to_cards_back(offset); q -= (N_words * n_cards_back); - assert(q >= _sp->bottom(), "Went below bottom!"); + assert(q >= _sp->bottom(), + err_msg("q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT, + q, _sp->bottom())); + assert(q < _sp->end(), + err_msg("q = " PTR_FORMAT " crossed above end = " PTR_FORMAT, + q, _sp->end())); index -= n_cards_back; offset = _array->offset_array(index); } assert(offset < N_words, "offset too large"); index--; q -= offset; + assert(q >= _sp->bottom(), + err_msg("q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT, + q, _sp->bottom())); + assert(q < _sp->end(), + err_msg("q = " PTR_FORMAT " crossed above end = " PTR_FORMAT, + q, _sp->end())); HeapWord* n = q; while (n <= addr) { debug_only(HeapWord* last = q); // for debugging q = n; n += _sp->block_size(n); - assert(n > q, err_msg("Looping at: " INTPTR_FORMAT, n)); + assert(n > q, + err_msg("Looping at n = " PTR_FORMAT " with last = " PTR_FORMAT " _sp = [" PTR_FORMAT "," PTR_FORMAT ")", + n, last, _sp->bottom(), _sp->end())); } assert(q <= addr, err_msg("wrong order for current (" INTPTR_FORMAT ") <= arg (" INTPTR_FORMAT ")", q, addr)); assert(addr <= n, err_msg("wrong order for arg (" INTPTR_FORMAT ") <= next (" INTPTR_FORMAT ")", addr, n)); diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp index 6dc44970726..8ed4e03d979 100644 --- a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp @@ -150,7 +150,9 @@ class CardTableModRefBS: public ModRefBarrierSet { // Mapping from address to card marking array entry jbyte* byte_for(const void* p) const { assert(_whole_heap.contains(p), - "out of bounds access to card marking array"); + err_msg("Attempt to access p = "PTR_FORMAT" out of bounds of " + " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")", + p, _whole_heap.start(), _whole_heap.end())); jbyte* result = &byte_map_base[uintptr_t(p) >> card_shift]; assert(result >= _byte_map && result < _byte_map + _byte_map_size, "out of bounds accessor for card marking array"); @@ -451,14 +453,18 @@ public: size_t delta = pointer_delta(p, byte_map_base, sizeof(jbyte)); HeapWord* result = (HeapWord*) (delta << card_shift); assert(_whole_heap.contains(result), - "out of bounds accessor from card marking array"); + err_msg("Returning result = "PTR_FORMAT" out of bounds of " + " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")", + result, _whole_heap.start(), _whole_heap.end())); return result; } // Mapping from address to card marking array index. size_t index_for(void* p) { assert(_whole_heap.contains(p), - "out of bounds access to card marking array"); + err_msg("Attempt to access p = "PTR_FORMAT" out of bounds of " + " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")", + p, _whole_heap.start(), _whole_heap.end())); return byte_for(p) - _byte_map; } From b7fbd6c9bff88298b11a457307abb173c4bc69d5 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 11 May 2011 16:12:01 -0700 Subject: [PATCH 36/65] 7043054: REGRESSION: JDK 7 b126 : Wrong userBounds in Paint.createContext() Reviewed-by: prr --- .../sun/java2d/opengl/OGLRenderer.java | 18 ++- .../classes/sun/java2d/pipe/AAShapePipe.java | 30 +++-- .../sun/java2d/pipe/AlphaColorPipe.java | 4 + .../sun/java2d/pipe/BufferedRenderPipe.java | 8 ++ .../classes/sun/java2d/pipe/LoopPipe.java | 4 + .../sun/java2d/pipe/ParallelogramPipe.java | 12 +- .../pipe/PixelToParallelogramConverter.java | 36 +++-- .../classes/sun/java2d/d3d/D3DRenderer.java | 18 ++- .../java/awt/Paint/PgramUserBoundsTest.java | 126 ++++++++++++++++++ 9 files changed, 226 insertions(+), 30 deletions(-) create mode 100644 jdk/test/java/awt/Paint/PgramUserBoundsTest.java diff --git a/jdk/src/share/classes/sun/java2d/opengl/OGLRenderer.java b/jdk/src/share/classes/sun/java2d/opengl/OGLRenderer.java index df9377e69e5..935c68c170e 100644 --- a/jdk/src/share/classes/sun/java2d/opengl/OGLRenderer.java +++ b/jdk/src/share/classes/sun/java2d/opengl/OGLRenderer.java @@ -102,15 +102,20 @@ class OGLRenderer extends BufferedRenderPipe { final ParallelogramPipe realpipe = oglr.getAAParallelogramPipe(); return new ParallelogramPipe() { public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) { GraphicsPrimitive.tracePrimitive("OGLFillAAParallelogram"); realpipe.fillParallelogram(sg2d, + ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2); } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, @@ -118,6 +123,7 @@ class OGLRenderer extends BufferedRenderPipe { { GraphicsPrimitive.tracePrimitive("OGLDrawAAParallelogram"); realpipe.drawParallelogram(sg2d, + ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2, lw1, lw2); } @@ -166,21 +172,29 @@ class OGLRenderer extends BufferedRenderPipe { oglr.fillSpans(sg2d, si, transx, transy); } public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) { GraphicsPrimitive.tracePrimitive("OGLFillParallelogram"); - oglr.fillParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2); + oglr.fillParallelogram(sg2d, + ux1, uy1, ux2, uy2, + x, y, dx1, dy1, dx2, dy2); } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2) { GraphicsPrimitive.tracePrimitive("OGLDrawParallelogram"); - oglr.drawParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2, lw1, lw2); + oglr.drawParallelogram(sg2d, + ux1, uy1, ux2, uy2, + x, y, dx1, dy1, dx2, dy2, lw1, lw2); } public void copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) diff --git a/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java b/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java index 6547f0e234e..eaa821d9022 100644 --- a/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java +++ b/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java @@ -68,21 +68,23 @@ public class AAShapePipe renderPath(sg, s, null); } - private static Rectangle2D computeBBox(double x, double y, - double dx1, double dy1, - double dx2, double dy2) + private static Rectangle2D computeBBox(double ux1, double uy1, + double ux2, double uy2) { - double lox, loy, hix, hiy; - lox = hix = x; - loy = hiy = y; - if (dx1 < 0) { lox += dx1; } else { hix += dx1; } - if (dy1 < 0) { loy += dy1; } else { hiy += dy1; } - if (dx2 < 0) { lox += dx2; } else { hix += dx2; } - if (dy2 < 0) { loy += dy2; } else { hiy += dy2; } - return new Rectangle2D.Double(lox, loy, hix-lox, hiy-loy); + if ((ux2 -= ux1) < 0) { + ux1 += ux2; + ux2 = -ux2; + } + if ((uy2 -= uy1) < 0) { + uy1 += uy2; + uy2 = -uy2; + } + return new Rectangle2D.Double(ux1, uy1, ux2, uy2); } public void fillParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -97,10 +99,12 @@ public class AAShapePipe return; } - renderTiles(sg, computeBBox(x, y, dx1, dy1, dx2, dy2), aatg, abox); + renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox); } public void drawParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, @@ -118,7 +122,7 @@ public class AAShapePipe // Note that bbox is of the original shape, not the wide path. // This is appropriate for handing to Paint methods... - renderTiles(sg, computeBBox(x, y, dx1, dy1, dx2, dy2), aatg, abox); + renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox); } private static byte[] theTile; diff --git a/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java b/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java index d19a895cb16..2b0ea08280e 100644 --- a/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java +++ b/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java @@ -66,6 +66,8 @@ public class AlphaColorPipe implements CompositePipe, ParallelogramPipe { } public void fillParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -75,6 +77,8 @@ public class AlphaColorPipe implements CompositePipe, ParallelogramPipe { } public void drawParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, diff --git a/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java b/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java index 675c8d71ff8..c5f30976958 100644 --- a/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java +++ b/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java @@ -408,6 +408,8 @@ public abstract class BufferedRenderPipe } public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -429,6 +431,8 @@ public abstract class BufferedRenderPipe } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, @@ -454,6 +458,8 @@ public abstract class BufferedRenderPipe private class AAParallelogramPipe implements ParallelogramPipe { public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -475,6 +481,8 @@ public abstract class BufferedRenderPipe } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, diff --git a/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java b/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java index 652239d784a..3df4adc893c 100644 --- a/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java +++ b/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java @@ -352,6 +352,8 @@ public class LoopPipe } public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -362,6 +364,8 @@ public class LoopPipe } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, diff --git a/jdk/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java b/jdk/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java index cefc09f10f2..57a9372ec13 100644 --- a/jdk/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java +++ b/jdk/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2011 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 @@ -40,9 +40,17 @@ import sun.java2d.SunGraphics2D; * => (x+dx2, y+dy2) * => origin * + * The four u[xy][12] parameters are the unsorted extreme coordinates + * of the primitive in user space. They may have been generated by a + * line or a rectangle so they could have u[xy]2 < u[xy]1 in some cases. + * They should be sorted before calculating the bounds of the original + * primitive (such as for calculating the user space bounds for the + * Paint.createContext() method). */ public interface ParallelogramPipe { public void fillParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2); @@ -59,6 +67,8 @@ public interface ParallelogramPipe { * difference between the outer and inner parallelograms. */ public void drawParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, diff --git a/jdk/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java b/jdk/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java index 3564fdf001e..86fcc5eaa17 100644 --- a/jdk/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java +++ b/jdk/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java @@ -175,8 +175,8 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter } public boolean drawGeneralLine(SunGraphics2D sg2d, - double x1, double y1, - double x2, double y2) + double ux1, double uy1, + double ux2, double uy2) { if (sg2d.strokeState == SunGraphics2D.STROKE_CUSTOM || sg2d.strokeState == SunGraphics2D.STROKE_THINDASHED) @@ -194,13 +194,14 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter double lw = bs.getLineWidth(); // Save the original dx, dy in case we need it to transform // the linewidth as a perpendicular vector below - double dx = x2 - x1; - double dy = y2 - y1; + double dx = ux2 - ux1; + double dy = uy2 - uy1; + double x1, y1, x2, y2; switch (sg2d.transformState) { case SunGraphics2D.TRANSFORM_GENERIC: case SunGraphics2D.TRANSFORM_TRANSLATESCALE: { - double coords[] = {x1, y1, x2, y2}; + double coords[] = {ux1, uy1, ux2, uy2}; sg2d.transform.transform(coords, 0, coords, 0, 2); x1 = coords[0]; y1 = coords[1]; @@ -213,13 +214,17 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter { double tx = sg2d.transform.getTranslateX(); double ty = sg2d.transform.getTranslateY(); - x1 += tx; - y1 += ty; - x2 += tx; - y2 += ty; + x1 = ux1 + tx; + y1 = uy1 + ty; + x2 = ux2 + tx; + y2 = uy2 + ty; } break; case SunGraphics2D.TRANSFORM_ISIDENT: + x1 = ux1; + y1 = uy1; + x2 = ux2; + y2 = uy2; break; default: throw new InternalError("unknown TRANSFORM state..."); @@ -279,7 +284,8 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter dx += udx; dy += udy; } - outrenderer.fillParallelogram(sg2d, px, py, -udy, udx, dx, dy); + outrenderer.fillParallelogram(sg2d, ux1, uy1, ux2, uy2, + px, py, -udy, udx, dx, dy); return true; } @@ -313,7 +319,8 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter px = newx; py = newy; } - outrenderer.fillParallelogram(sg2d, px, py, dx1, dy1, dx2, dy2); + outrenderer.fillParallelogram(sg2d, rx, ry, rx+rw, ry+rh, + px, py, dx1, dy1, dx2, dy2); } public void drawRectangle(SunGraphics2D sg2d, @@ -360,10 +367,12 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter // entire hole in the middle of the parallelogram // so we can just fill the outer parallelogram. fillOuterParallelogram(sg2d, + rx, ry, rx+rw, ry+rh, px, py, dx1, dy1, dx2, dy2, len1, len2, lw1, lw2); } else { outrenderer.drawParallelogram(sg2d, + rx, ry, rx+rw, ry+rh, px, py, dx1, dy1, dx2, dy2, lw1 / len1, lw2 / len2); } @@ -377,6 +386,8 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter * and issues a single fillParallelogram request to fill it. */ public void fillOuterParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double px, double py, double dx1, double dy1, double dx2, double dy2, @@ -412,6 +423,7 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter dx2 += udx2; dy2 += udy2; - outrenderer.fillParallelogram(sg2d, px, py, dx1, dy1, dx2, dy2); + outrenderer.fillParallelogram(sg2d, ux1, uy1, ux2, uy2, + px, py, dx1, dy1, dx2, dy2); } } diff --git a/jdk/src/windows/classes/sun/java2d/d3d/D3DRenderer.java b/jdk/src/windows/classes/sun/java2d/d3d/D3DRenderer.java index cd4e40df92a..1fc4f661535 100644 --- a/jdk/src/windows/classes/sun/java2d/d3d/D3DRenderer.java +++ b/jdk/src/windows/classes/sun/java2d/d3d/D3DRenderer.java @@ -102,15 +102,20 @@ class D3DRenderer extends BufferedRenderPipe { final ParallelogramPipe realpipe = d3dr.getAAParallelogramPipe(); return new ParallelogramPipe() { public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) { GraphicsPrimitive.tracePrimitive("D3DFillAAParallelogram"); realpipe.fillParallelogram(sg2d, + ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2); } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, @@ -118,6 +123,7 @@ class D3DRenderer extends BufferedRenderPipe { { GraphicsPrimitive.tracePrimitive("D3DDrawAAParallelogram"); realpipe.drawParallelogram(sg2d, + ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2, lw1, lw2); } @@ -167,21 +173,29 @@ class D3DRenderer extends BufferedRenderPipe { d3dr.fillSpans(sg2d, si, transx, transy); } public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) { GraphicsPrimitive.tracePrimitive("D3DFillParallelogram"); - d3dr.fillParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2); + d3dr.fillParallelogram(sg2d, + ux1, uy1, ux2, uy2, + x, y, dx1, dy1, dx2, dy2); } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2) { GraphicsPrimitive.tracePrimitive("D3DDrawParallelogram"); - d3dr.drawParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2, lw1, lw2); + d3dr.drawParallelogram(sg2d, + ux1, uy1, ux2, uy2, + x, y, dx1, dy1, dx2, dy2, lw1, lw2); } public void copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) diff --git a/jdk/test/java/awt/Paint/PgramUserBoundsTest.java b/jdk/test/java/awt/Paint/PgramUserBoundsTest.java new file mode 100644 index 00000000000..9c71f348219 --- /dev/null +++ b/jdk/test/java/awt/Paint/PgramUserBoundsTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011, 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. + */ + +/** + * @test + * @bug 7043054 + * @summary Verifies that Paint objects receive the appropriate user space + * bounds in their createContext() method + * @run main PgramUserBoundsTest + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.PaintContext; +import java.awt.RenderingHints; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; + +public class PgramUserBoundsTest { + static final int MinX = 10; + static final int MinY = 20; + static final int MaxX = 30; + static final int MaxY = 50; + static AffineTransform identity = new AffineTransform(); + + public static void main(String argv[]) { + BufferedImage bimg = + new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bimg.createGraphics(); + g2d.setPaint(new BoundsCheckerPaint(MinX, MinY, MaxX, MaxY)); + testAll(g2d); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + testAll(g2d); + } + + static void testAll(Graphics2D g2d) { + g2d.setTransform(identity); + g2d.translate(100, 100); + testPrimitives(g2d); + + g2d.setTransform(identity); + g2d.scale(10, 10); + testPrimitives(g2d); + + g2d.setTransform(identity); + g2d.rotate(Math.PI/6); + testPrimitives(g2d); + } + + static void testPrimitives(Graphics2D g2d) { + testLine(g2d); + testRect(g2d); + } + + static void testLine(Graphics2D g2d) { + testLine(g2d, MinX, MinY, MaxX, MaxY); + testLine(g2d, MaxX, MinY, MinX, MaxY); + testLine(g2d, MinX, MaxY, MaxX, MinY); + testLine(g2d, MaxX, MaxY, MinX, MinY); + } + + static void testRect(Graphics2D g2d) { + g2d.fillRect(MinX, MinY, MaxX - MinX, MaxY - MinY); + g2d.fill(new Rectangle(MinX, MinY, MaxX - MinX, MaxY - MinY)); + } + + static void testLine(Graphics2D g2d, int x1, int y1, int x2, int y2) { + g2d.drawLine(x1, y1, x2, y2); + g2d.draw(new Line2D.Double(x1, y1, x2, y2)); + } + + static class BoundsCheckerPaint implements Paint { + private Color c = Color.WHITE; + private Rectangle2D expectedBounds; + + public BoundsCheckerPaint(double x1, double y1, + double x2, double y2) + { + expectedBounds = new Rectangle2D.Double(); + expectedBounds.setFrameFromDiagonal(x1, y1, x2, y2); + } + + public int getTransparency() { + return c.getTransparency(); + } + + public PaintContext createContext(ColorModel cm, + Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + System.out.println("user bounds = "+userBounds); + if (!userBounds.equals(expectedBounds)) { + throw new RuntimeException("bounds fail to match"); + } + return c.createContext(cm, deviceBounds, userBounds, xform, hints); + } + } +} From 91db4e26f79baacdc80d027084800db48848a0eb Mon Sep 17 00:00:00 2001 From: Keith McGuigan Date: Wed, 11 May 2011 20:18:13 -0400 Subject: [PATCH 37/65] 6659215: javax.management.timer.Timer frequently fails to start Copy array to avoid ConcurrentModificationException Reviewed-by: dcubed, alanb --- .../classes/javax/management/timer/Timer.java | 6 +- .../javax/management/timer/StartTest.java | 97 +++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 jdk/test/javax/management/timer/StartTest.java diff --git a/jdk/src/share/classes/javax/management/timer/Timer.java b/jdk/src/share/classes/javax/management/timer/Timer.java index e5e2904676f..2516de5259e 100644 --- a/jdk/src/share/classes/javax/management/timer/Timer.java +++ b/jdk/src/share/classes/javax/management/timer/Timer.java @@ -26,6 +26,7 @@ package javax.management.timer; import static com.sun.jmx.defaults.JmxProperties.TIMER_LOGGER; +import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; import java.util.Iterator; @@ -1003,7 +1004,10 @@ public class Timer extends NotificationBroadcasterSupport Integer notifID; Date date; - for (Object[] obj : timerTable.values()) { + ArrayList values = + new ArrayList(timerTable.values()); + + for (Object[] obj : values) { // Retrieve the timer notification and the date notification. // diff --git a/jdk/test/javax/management/timer/StartTest.java b/jdk/test/javax/management/timer/StartTest.java new file mode 100644 index 00000000000..96ab67a0d14 --- /dev/null +++ b/jdk/test/javax/management/timer/StartTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2008, 2011, 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. + */ + +/* + * @bug 6659215 + * @summary Test on timer start method with past notifications + * @author Shanliang JIANG + * @run clean StartTest + * @run build StartTest + * @run main StartTest + */ + +import java.util.Date; +import javax.management.timer.Timer; +import javax.management.Notification; +import javax.management.NotificationListener; + +public class StartTest { + public static void main(String[] args) throws Exception { + System.out.println( + ">>> Test on timer start method with past notifications."); + + System.out.println(">>> Create a Timer object."); + Timer timer = new Timer(); + + System.out.println( + ">>> Set the flag (setSendPastNotification) to true."); + timer.setSendPastNotifications(true); + + timer.addNotificationListener(myListener, null, null); + + System.out.println(">>> Add notifications: " + SENT); + + Date date = new Date(); + for (int i = 0; i < SENT; i++) { + timer.addNotification( + "testType" + i, "testMsg" + i, "testData" + i, date); + } + + System.out.println(">>> The notifications should be sent at " + date); + System.out.println(">>> Sleep 100 ms to have past notifications."); + Thread.sleep(100); + + System.out.println(">>> Start the timer at " + new Date()); + timer.start(); + + System.out.println(">>> Stop the timer."); + Thread.sleep(100); + stopping = true; + timer.stop(); + + if (received != SENT) { + throw new RuntimeException( + "Expected to receive " + SENT + " but got " + received); + } + + System.out.println(">>> Received all expected notifications."); + + System.out.println(">>> Bye bye!"); + } + + private static NotificationListener myListener = + new NotificationListener() { + public void handleNotification(Notification n, Object hb) { + if (!stopping) { + received++; + System.out.println( + ">>> myListener-handleNotification: received " + + n.getSequenceNumber()); + } + } + }; + + private static int SENT = 10; + private static volatile int received = 0; + private static volatile boolean stopping = false; +} From 36415e8393b64183dc84d114291a269fd56b2e39 Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Wed, 11 May 2011 20:39:40 -0700 Subject: [PATCH 38/65] 7043514: NPE in sun.security.ssl.JsseJce.isEcAvailable Reviewed-by: weijun, vinnie, wetmore --- jdk/src/share/classes/sun/security/ssl/JsseJce.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jdk/src/share/classes/sun/security/ssl/JsseJce.java b/jdk/src/share/classes/sun/security/ssl/JsseJce.java index fbbbf5d2b38..d91b8f4ea4e 100644 --- a/jdk/src/share/classes/sun/security/ssl/JsseJce.java +++ b/jdk/src/share/classes/sun/security/ssl/JsseJce.java @@ -62,7 +62,7 @@ final class JsseJce { // Flag indicating whether EC crypto is available. // If null, then we have not checked yet. // If yes, then all the EC based crypto we need is available. - private static volatile Boolean ecAvailable; + private static Boolean ecAvailable; // Flag indicating whether Kerberos crypto is available. // If true, then all the Kerberos-based crypto we need is available. @@ -190,7 +190,7 @@ final class JsseJce { // no instantiation of this class } - static boolean isEcAvailable() { + synchronized static boolean isEcAvailable() { if (ecAvailable == null) { try { JsseJce.getSignature(SIGNATURE_ECDSA); @@ -206,7 +206,7 @@ final class JsseJce { return ecAvailable; } - static void clearEcAvailable() { + synchronized static void clearEcAvailable() { ecAvailable = null; } From 2ba6bd02d21b9d8d7a47d5ed12fd1dd3b7418b1b Mon Sep 17 00:00:00 2001 From: Keith McGuigan Date: Thu, 12 May 2011 08:17:29 -0400 Subject: [PATCH 39/65] 7044203: Missing @test tag in test/javax/management/timer/StartTest.java Add tag Reviewed-by: alanb --- jdk/test/javax/management/timer/StartTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/test/javax/management/timer/StartTest.java b/jdk/test/javax/management/timer/StartTest.java index 96ab67a0d14..1b7b66a8b05 100644 --- a/jdk/test/javax/management/timer/StartTest.java +++ b/jdk/test/javax/management/timer/StartTest.java @@ -22,6 +22,7 @@ */ /* + * @test * @bug 6659215 * @summary Test on timer start method with past notifications * @author Shanliang JIANG From 30a20e1d6217ac8a9068fda7ca4d5028693ae8f8 Mon Sep 17 00:00:00 2001 From: Kelly O'Hair Date: Thu, 12 May 2011 07:24:37 -0700 Subject: [PATCH 40/65] 7043921: generate java-rmi.cgi on 64 bit platform Reviewed-by: omajid, katleman --- jdk/make/sun/rmi/rmi/Makefile | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/jdk/make/sun/rmi/rmi/Makefile b/jdk/make/sun/rmi/rmi/Makefile index 35e4163c5a8..127777229e4 100644 --- a/jdk/make/sun/rmi/rmi/Makefile +++ b/jdk/make/sun/rmi/rmi/Makefile @@ -85,16 +85,21 @@ REMOTE_impls = \ sun.rmi.registry.RegistryImpl \ sun.rmi.transport.DGCImpl -ifeq ($(PLATFORM), windows) -build: stubs -else # PLATFORM -ifneq ($(ARCH_DATA_MODEL), 32) -build: stubs -else # ARCH_DATA_MODEL -build: stubs bin +# +# The java-rmi.cgi script in bin/ only gets delivered in certain situations +# +BUILD_TARGETS = stubs +ifeq ($(PLATFORM), linux) + BUILD_TARGETS += bin endif +ifeq ($(PLATFORM), solaris) + ifeq ($(ARCH_DATA_MODEL), 32) + BUILD_TARGETS += bin + endif endif +build: $(BUILD_TARGETS) + clean clobber:: bin.clean From e5357d4bee959e8e4aa5f4ef5383ae4eb9c41803 Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Thu, 12 May 2011 10:29:02 -0700 Subject: [PATCH 41/65] 7043461: VM crashes in void LinkResolver::runtime_resolve_virtual_method Reviewed-by: kvn, coleenp --- .../src/cpu/sparc/vm/cppInterpreter_sparc.cpp | 1 + .../src/cpu/sparc/vm/interpreter_sparc.cpp | 19 ------ .../sparc/vm/templateInterpreter_sparc.cpp | 44 ++++++++----- hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp | 17 +++--- hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp | 20 ------ hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp | 14 ----- .../cpu/x86/vm/templateInterpreter_x86_32.cpp | 1 + .../cpu/x86/vm/templateInterpreter_x86_64.cpp | 1 + .../src/cpu/zero/vm/cppInterpreter_zero.cpp | 1 + hotspot/src/cpu/zero/vm/interpreter_zero.cpp | 18 ------ .../vm/interpreter/abstractInterpreter.hpp | 31 +++++++--- .../src/share/vm/runtime/deoptimization.cpp | 61 +++++++++++-------- .../src/share/vm/runtime/deoptimization.hpp | 6 ++ hotspot/src/share/vm/runtime/frame.cpp | 19 +++++- hotspot/src/share/vm/runtime/vframeArray.cpp | 14 +++-- hotspot/src/share/vm/runtime/vframeArray.hpp | 8 ++- 16 files changed, 135 insertions(+), 140 deletions(-) diff --git a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp index f3a3f10ea80..46d7d27a259 100644 --- a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp @@ -2176,6 +2176,7 @@ int AbstractInterpreter::layout_activation(methodOop method, int tempcount, // Number of slots on java expression stack in use int popframe_extra_args, int moncount, // Number of active monitors + int caller_actual_parameters, int callee_param_size, int callee_locals_size, frame* caller, diff --git a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp index f90f28873e8..774c3b64d45 100644 --- a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp @@ -423,25 +423,6 @@ bool AbstractInterpreter::can_be_compiled(methodHandle m) { return true; } -// This method tells the deoptimizer how big an interpreted frame must be: -int AbstractInterpreter::size_activation(methodOop method, - int tempcount, - int popframe_extra_args, - int moncount, - int callee_param_count, - int callee_locals, - bool is_top_frame) { - return layout_activation(method, - tempcount, - popframe_extra_args, - moncount, - callee_param_count, - callee_locals, - (frame*)NULL, - (frame*)NULL, - is_top_frame); -} - void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp index ad53f8834a0..efc0a46c872 100644 --- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp @@ -1623,6 +1623,7 @@ int AbstractInterpreter::layout_activation(methodOop method, int tempcount, int popframe_extra_args, int moncount, + int caller_actual_parameters, int callee_param_count, int callee_local_count, frame* caller, @@ -1698,24 +1699,35 @@ int AbstractInterpreter::layout_activation(methodOop method, popframe_extra_args; int local_words = method->max_locals() * Interpreter::stackElementWords; + NEEDS_CLEANUP; intptr_t* locals; - if (caller->is_compiled_frame()) { - // Compiled frames do not allocate a varargs area so place them - // next to the register save area. - locals = fp + frame::register_save_words + local_words - 1; - // Caller wants his own SP back - int caller_frame_size = caller->cb()->frame_size(); - *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS; + if (caller->is_interpreted_frame()) { + // Can force the locals area to end up properly overlapping the top of the expression stack. + intptr_t* Lesp_ptr = caller->interpreter_frame_tos_address() - 1; + // Note that this computation means we replace size_of_parameters() values from the caller + // interpreter frame's expression stack with our argument locals + int parm_words = caller_actual_parameters * Interpreter::stackElementWords; + locals = Lesp_ptr + parm_words; + int delta = local_words - parm_words; + int computed_sp_adjustment = (delta > 0) ? round_to(delta, WordsPerLong) : 0; + *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + computed_sp_adjustment) - STACK_BIAS; } else { - assert(caller->is_interpreted_frame() || caller->is_entry_frame(), "only possible cases"); - // The entry and interpreter frames are laid out like normal C - // frames so place the locals adjacent to the varargs area. - locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; - if (caller->is_interpreted_frame()) { - int parm_words = method->size_of_parameters() * Interpreter::stackElementWords; - int delta = local_words - parm_words; - int computed_sp_adjustment = (delta > 0) ? round_to(delta, WordsPerLong) : 0; - *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + computed_sp_adjustment) - STACK_BIAS; + assert(caller->is_compiled_frame() || caller->is_entry_frame(), "only possible cases"); + // Don't have Lesp available; lay out locals block in the caller + // adjacent to the register window save area. + // + // Compiled frames do not allocate a varargs area which is why this if + // statement is needed. + // + if (caller->is_compiled_frame()) { + locals = fp + frame::register_save_words + local_words - 1; + } else { + locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; + } + if (!caller->is_entry_frame()) { + // Caller wants his own SP back + int caller_frame_size = caller->cb()->frame_size(); + *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS; } } if (TraceDeoptimization) { diff --git a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp index edacb282edb..226c6cbc6e6 100644 --- a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp +++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp @@ -2339,14 +2339,15 @@ void BytecodeInterpreter::layout_interpreterState(interpreterState to_fill, } int AbstractInterpreter::layout_activation(methodOop method, - int tempcount, // - int popframe_extra_args, - int moncount, - int callee_param_count, - int callee_locals, - frame* caller, - frame* interpreter_frame, - bool is_top_frame) { + int tempcount, // + int popframe_extra_args, + int moncount, + int caller_actual_parameters, + int callee_param_count, + int callee_locals, + frame* caller, + frame* interpreter_frame, + bool is_top_frame) { assert(popframe_extra_args == 0, "FIX ME"); // NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state() diff --git a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp index cb2345a4183..43a5a18a5b6 100644 --- a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp @@ -242,26 +242,6 @@ address InterpreterGenerator::generate_method_handle_entry(void) { return entry_point; } - -// This method tells the deoptimizer how big an interpreted frame must be: -int AbstractInterpreter::size_activation(methodOop method, - int tempcount, - int popframe_extra_args, - int moncount, - int callee_param_count, - int callee_locals, - bool is_top_frame) { - return layout_activation(method, - tempcount, - popframe_extra_args, - moncount, - callee_param_count, - callee_locals, - (frame*) NULL, - (frame*) NULL, - is_top_frame); -} - void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in diff --git a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp index 3dbea5754dc..1c124c2f0b7 100644 --- a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp @@ -362,20 +362,6 @@ address InterpreterGenerator::generate_empty_entry(void) { } -// This method tells the deoptimizer how big an interpreted frame must be: -int AbstractInterpreter::size_activation(methodOop method, - int tempcount, - int popframe_extra_args, - int moncount, - int callee_param_count, - int callee_locals, - bool is_top_frame) { - return layout_activation(method, - tempcount, popframe_extra_args, moncount, - callee_param_count, callee_locals, - (frame*) NULL, (frame*) NULL, is_top_frame); -} - void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp index a7222c265a5..6184bc393b5 100644 --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp @@ -1589,6 +1589,7 @@ int AbstractInterpreter::layout_activation(methodOop method, int tempcount, int popframe_extra_args, int moncount, + int caller_actual_parameters, int callee_param_count, int callee_locals, frame* caller, diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp index 8d3740c7049..80f08524b06 100644 --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp @@ -1603,6 +1603,7 @@ int AbstractInterpreter::layout_activation(methodOop method, int tempcount, int popframe_extra_args, int moncount, + int caller_actual_parameters, int callee_param_count, int callee_locals, frame* caller, diff --git a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp index 13a4710a104..961de9fdd96 100644 --- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp +++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp @@ -1427,6 +1427,7 @@ int AbstractInterpreter::layout_activation(methodOop method, int tempcount, int popframe_extra_args, int moncount, + int caller_actual_parameters, int callee_param_count, int callee_locals, frame* caller, diff --git a/hotspot/src/cpu/zero/vm/interpreter_zero.cpp b/hotspot/src/cpu/zero/vm/interpreter_zero.cpp index ff5a69be88e..93c3b90fa45 100644 --- a/hotspot/src/cpu/zero/vm/interpreter_zero.cpp +++ b/hotspot/src/cpu/zero/vm/interpreter_zero.cpp @@ -82,24 +82,6 @@ bool AbstractInterpreter::can_be_compiled(methodHandle m) { return true; } -int AbstractInterpreter::size_activation(methodOop method, - int tempcount, - int popframe_extra_args, - int moncount, - int callee_param_count, - int callee_locals, - bool is_top_frame) { - return layout_activation(method, - tempcount, - popframe_extra_args, - moncount, - callee_param_count, - callee_locals, - (frame*) NULL, - (frame*) NULL, - is_top_frame); -} - void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { } diff --git a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp index c407fbaba66..a792812954f 100644 --- a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp +++ b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp @@ -175,19 +175,32 @@ class AbstractInterpreter: AllStatic { int temps, int popframe_args, int monitors, + int caller_actual_parameters, int callee_params, int callee_locals, - bool is_top_frame); + bool is_top_frame) { + return layout_activation(method, + temps, + popframe_args, + monitors, + caller_actual_parameters, + callee_params, + callee_locals, + (frame*)NULL, + (frame*)NULL, + is_top_frame); + } static int layout_activation(methodOop method, - int temps, - int popframe_args, - int monitors, - int callee_params, - int callee_locals, - frame* caller, - frame* interpreter_frame, - bool is_top_frame); + int temps, + int popframe_args, + int monitors, + int caller_actual_parameters, + int callee_params, + int callee_locals, + frame* caller, + frame* interpreter_frame, + bool is_top_frame); // Runtime support static bool is_not_reached( methodHandle method, int bci); diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp index 6313c547609..b2f172d1d70 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.cpp +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp @@ -90,12 +90,14 @@ bool DeoptimizationMarker::_is_active = false; Deoptimization::UnrollBlock::UnrollBlock(int size_of_deoptimized_frame, int caller_adjustment, + int caller_actual_parameters, int number_of_frames, intptr_t* frame_sizes, address* frame_pcs, BasicType return_type) { _size_of_deoptimized_frame = size_of_deoptimized_frame; _caller_adjustment = caller_adjustment; + _caller_actual_parameters = caller_actual_parameters; _number_of_frames = number_of_frames; _frame_sizes = frame_sizes; _frame_pcs = frame_pcs; @@ -373,6 +375,28 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread popframe_extra_args = in_words(thread->popframe_preserved_args_size_in_words()); } + // Find the current pc for sender of the deoptee. Since the sender may have been deoptimized + // itself since the deoptee vframeArray was created we must get a fresh value of the pc rather + // than simply use array->sender.pc(). This requires us to walk the current set of frames + // + frame deopt_sender = stub_frame.sender(&dummy_map); // First is the deoptee frame + deopt_sender = deopt_sender.sender(&dummy_map); // Now deoptee caller + + // It's possible that the number of paramters at the call site is + // different than number of arguments in the callee when method + // handles are used. If the caller is interpreted get the real + // value so that the proper amount of space can be added to it's + // frame. + int caller_actual_parameters = callee_parameters; + if (deopt_sender.is_interpreted_frame()) { + methodHandle method = deopt_sender.interpreter_frame_method(); + Bytecode_invoke cur = Bytecode_invoke_check(method, + deopt_sender.interpreter_frame_bci()); + Symbol* signature = method->constants()->signature_ref_at(cur.index()); + ArgumentSizeComputer asc(signature); + caller_actual_parameters = asc.size() + (cur.has_receiver() ? 1 : 0); + } + // // frame_sizes/frame_pcs[0] oldest frame (int or c2i) // frame_sizes/frame_pcs[1] next oldest frame (int) @@ -391,7 +415,13 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // frame[number_of_frames - 1 ] = on_stack_size(youngest) // frame[number_of_frames - 2 ] = on_stack_size(sender(youngest)) // frame[number_of_frames - 3 ] = on_stack_size(sender(sender(youngest))) - frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(callee_parameters, + int caller_parms = callee_parameters; + if (index == array->frames() - 1) { + // Use the value from the interpreted caller + caller_parms = caller_actual_parameters; + } + frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(caller_parms, + callee_parameters, callee_locals, index == 0, popframe_extra_args); @@ -418,28 +448,6 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // Compute information for handling adapters and adjusting the frame size of the caller. int caller_adjustment = 0; - // Find the current pc for sender of the deoptee. Since the sender may have been deoptimized - // itself since the deoptee vframeArray was created we must get a fresh value of the pc rather - // than simply use array->sender.pc(). This requires us to walk the current set of frames - // - frame deopt_sender = stub_frame.sender(&dummy_map); // First is the deoptee frame - deopt_sender = deopt_sender.sender(&dummy_map); // Now deoptee caller - - // It's possible that the number of paramters at the call site is - // different than number of arguments in the callee when method - // handles are used. If the caller is interpreted get the real - // value so that the proper amount of space can be added to it's - // frame. - int sender_callee_parameters = callee_parameters; - if (deopt_sender.is_interpreted_frame()) { - methodHandle method = deopt_sender.interpreter_frame_method(); - Bytecode_invoke cur = Bytecode_invoke_check(method, - deopt_sender.interpreter_frame_bci()); - Symbol* signature = method->constants()->signature_ref_at(cur.index()); - ArgumentSizeComputer asc(signature); - sender_callee_parameters = asc.size() + (cur.has_receiver() ? 1 : 0); - } - // Compute the amount the oldest interpreter frame will have to adjust // its caller's stack by. If the caller is a compiled frame then // we pretend that the callee has no parameters so that the @@ -454,11 +462,11 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread if (deopt_sender.is_compiled_frame()) { caller_adjustment = last_frame_adjust(0, callee_locals); - } else if (callee_locals > sender_callee_parameters) { + } else if (callee_locals > caller_actual_parameters) { // The caller frame may need extending to accommodate // non-parameter locals of the first unpacked interpreted frame. // Compute that adjustment. - caller_adjustment = last_frame_adjust(sender_callee_parameters, callee_locals); + caller_adjustment = last_frame_adjust(caller_actual_parameters, callee_locals); } // If the sender is deoptimized the we must retrieve the address of the handler @@ -473,6 +481,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread UnrollBlock* info = new UnrollBlock(array->frame_size() * BytesPerWord, caller_adjustment * BytesPerWord, + caller_actual_parameters, number_of_frames, frame_sizes, frame_pcs, @@ -570,7 +579,7 @@ JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_m UnrollBlock* info = array->unroll_block(); // Unpack the interpreter frames and any adapter frame (c2 only) we might create. - array->unpack_to_stack(stub_frame, exec_mode); + array->unpack_to_stack(stub_frame, exec_mode, info->caller_actual_parameters()); BasicType bt = info->return_type(); diff --git a/hotspot/src/share/vm/runtime/deoptimization.hpp b/hotspot/src/share/vm/runtime/deoptimization.hpp index 7f6c8c37056..853cd9a017e 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.hpp +++ b/hotspot/src/share/vm/runtime/deoptimization.hpp @@ -138,6 +138,9 @@ class Deoptimization : AllStatic { intptr_t* _register_block; // Block for storing callee-saved registers. BasicType _return_type; // Tells if we have to restore double or long return value intptr_t _initial_fp; // FP of the sender frame + int _caller_actual_parameters; // The number of actual arguments at the + // interpreted caller of the deoptimized frame + // The following fields are used as temps during the unpacking phase // (which is tight on registers, especially on x86). They really ought // to be PD variables but that involves moving this class into its own @@ -149,6 +152,7 @@ class Deoptimization : AllStatic { // Constructor UnrollBlock(int size_of_deoptimized_frame, int caller_adjustment, + int caller_actual_parameters, int number_of_frames, intptr_t* frame_sizes, address* frames_pcs, @@ -168,6 +172,8 @@ class Deoptimization : AllStatic { void set_initial_fp(intptr_t fp) { _initial_fp = fp; } + int caller_actual_parameters() const { return _caller_actual_parameters; } + // Accessors used by the code generator for the unpack stub. static int size_of_deoptimized_frame_offset_in_bytes() { return offset_of(UnrollBlock, _size_of_deoptimized_frame); } static int caller_adjustment_offset_in_bytes() { return offset_of(UnrollBlock, _caller_adjustment); } diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp index d5f490272b7..e55543311e2 100644 --- a/hotspot/src/share/vm/runtime/frame.cpp +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -1452,13 +1452,26 @@ void FrameValues::validate() { void FrameValues::print() { _values.sort(compare); - intptr_t* v0 = _values.at(0).location; - intptr_t* v1 = _values.at(_values.length() - 1).location; + JavaThread* thread = JavaThread::current(); + + // Sometimes values like the fp can be invalid values if the + // register map wasn't updated during the walk. Trim out values + // that aren't actually in the stack of the thread. + int min_index = 0; + int max_index = _values.length() - 1; + intptr_t* v0 = _values.at(min_index).location; + while (!thread->is_in_stack((address)v0)) { + v0 = _values.at(++min_index).location; + } + intptr_t* v1 = _values.at(max_index).location; + while (!thread->is_in_stack((address)v1)) { + v1 = _values.at(--max_index).location; + } intptr_t* min = MIN2(v0, v1); intptr_t* max = MAX2(v0, v1); intptr_t* cur = max; intptr_t* last = NULL; - for (int i = _values.length() - 1; i >= 0; i--) { + for (int i = max_index; i >= min_index; i--) { FrameValue fv = _values.at(i); while (cur > fv.location) { tty->print_cr(" " INTPTR_FORMAT ": " INTPTR_FORMAT, cur, *cur); diff --git a/hotspot/src/share/vm/runtime/vframeArray.cpp b/hotspot/src/share/vm/runtime/vframeArray.cpp index 2a16535310a..52b08099922 100644 --- a/hotspot/src/share/vm/runtime/vframeArray.cpp +++ b/hotspot/src/share/vm/runtime/vframeArray.cpp @@ -154,7 +154,8 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { int unpack_counter = 0; -void vframeArrayElement::unpack_on_stack(int callee_parameters, +void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, + int callee_parameters, int callee_locals, frame* caller, bool is_top_frame, @@ -270,6 +271,7 @@ void vframeArrayElement::unpack_on_stack(int callee_parameters, temps + callee_parameters, popframe_preserved_args_size_in_words, locks, + caller_actual_parameters, callee_parameters, callee_locals, caller, @@ -415,7 +417,8 @@ void vframeArrayElement::unpack_on_stack(int callee_parameters, } -int vframeArrayElement::on_stack_size(int callee_parameters, +int vframeArrayElement::on_stack_size(int caller_actual_parameters, + int callee_parameters, int callee_locals, bool is_top_frame, int popframe_extra_stack_expression_els) const { @@ -426,6 +429,7 @@ int vframeArrayElement::on_stack_size(int callee_parameters, temps + callee_parameters, popframe_extra_stack_expression_els, locks, + caller_actual_parameters, callee_parameters, callee_locals, is_top_frame); @@ -496,7 +500,7 @@ void vframeArray::fill_in(JavaThread* thread, } } -void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode) { +void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode, int caller_actual_parameters) { // stack picture // unpack_frame // [new interpreter frames ] (frames are skeletal but walkable) @@ -525,7 +529,8 @@ void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode) { for (index = frames() - 1; index >= 0 ; index--) { int callee_parameters = index == 0 ? 0 : element(index-1)->method()->size_of_parameters(); int callee_locals = index == 0 ? 0 : element(index-1)->method()->max_locals(); - element(index)->unpack_on_stack(callee_parameters, + element(index)->unpack_on_stack(caller_actual_parameters, + callee_parameters, callee_locals, &caller_frame, index == 0, @@ -534,6 +539,7 @@ void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode) { Deoptimization::unwind_callee_save_values(element(index)->iframe(), this); } caller_frame = *element(index)->iframe(); + caller_actual_parameters = callee_parameters; } diff --git a/hotspot/src/share/vm/runtime/vframeArray.hpp b/hotspot/src/share/vm/runtime/vframeArray.hpp index c45dfb73aa9..7072d22e074 100644 --- a/hotspot/src/share/vm/runtime/vframeArray.hpp +++ b/hotspot/src/share/vm/runtime/vframeArray.hpp @@ -83,13 +83,15 @@ class vframeArrayElement : public _ValueObj { // Returns the on stack word size for this frame // callee_parameters is the number of callee locals residing inside this frame - int on_stack_size(int callee_parameters, + int on_stack_size(int caller_actual_parameters, + int callee_parameters, int callee_locals, bool is_top_frame, int popframe_extra_stack_expression_els) const; // Unpacks the element to skeletal interpreter frame - void unpack_on_stack(int callee_parameters, + void unpack_on_stack(int caller_actual_parameters, + int callee_parameters, int callee_locals, frame* caller, bool is_top_frame, @@ -190,7 +192,7 @@ class vframeArray: public CHeapObj { int frame_size() const { return _frame_size; } // Unpack the array on the stack passed in stack interval - void unpack_to_stack(frame &unpack_frame, int exec_mode); + void unpack_to_stack(frame &unpack_frame, int exec_mode, int caller_actual_parameters); // Deallocates monitor chunks allocated during deoptimization. // This should be called when the array is not used anymore. From b38843c8d9f0c6d6799d6053a851a974412c9e1f Mon Sep 17 00:00:00 2001 From: Frederic Parain Date: Thu, 12 May 2011 10:30:11 -0700 Subject: [PATCH 42/65] 7036199: Adding a notification to the implementation of GarbageCollectorMXBeans Add a notification to the GarbageCollectorMXBeans Reviewed-by: acorn, mchung --- hotspot/src/share/vm/classfile/vmSymbols.hpp | 8 + .../concurrentMarkSweepGeneration.cpp | 17 +- .../concurrentMarkSweepGeneration.hpp | 4 +- .../gc_implementation/g1/g1CollectedHeap.cpp | 4 +- .../parallelScavenge/psMarkSweep.cpp | 2 +- .../parallelScavenge/psParallelCompact.cpp | 2 +- .../parallelScavenge/psScavenge.cpp | 2 +- .../src/share/vm/memory/genCollectedHeap.cpp | 2 +- .../src/share/vm/runtime/serviceThread.cpp | 11 +- hotspot/src/share/vm/services/gcNotifier.cpp | 216 ++++++++++++++++++ hotspot/src/share/vm/services/gcNotifier.hpp | 69 ++++++ hotspot/src/share/vm/services/jmm.h | 5 +- hotspot/src/share/vm/services/management.cpp | 29 ++- hotspot/src/share/vm/services/management.hpp | 4 + .../src/share/vm/services/memoryManager.cpp | 10 +- .../src/share/vm/services/memoryManager.hpp | 5 +- .../src/share/vm/services/memoryService.cpp | 16 +- .../src/share/vm/services/memoryService.hpp | 18 +- 18 files changed, 395 insertions(+), 29 deletions(-) create mode 100644 hotspot/src/share/vm/services/gcNotifier.cpp create mode 100644 hotspot/src/share/vm/services/gcNotifier.hpp diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index 88d946854da..526ba6455ea 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -471,6 +471,13 @@ template(sun_management_ManagementFactory, "sun/management/ManagementFactory") \ template(sun_management_Sensor, "sun/management/Sensor") \ template(sun_management_Agent, "sun/management/Agent") \ + template(sun_management_GarbageCollectorImpl, "sun/management/GarbageCollectorImpl") \ + template(getGcInfoBuilder_name, "getGcInfoBuilder") \ + template(getGcInfoBuilder_signature, "()Lsun/management/GcInfoBuilder;") \ + template(com_sun_management_GcInfo, "com/sun/management/GcInfo") \ + template(com_sun_management_GcInfo_constructor_signature, "(Lsun/management/GcInfoBuilder;JJJ[Ljava/lang/management/MemoryUsage;[Ljava/lang/management/MemoryUsage;[Ljava/lang/Object;)V") \ + template(createGCNotification_name, "createGCNotification") \ + template(createGCNotification_signature, "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/sun/management/GcInfo;)V") \ template(createMemoryPoolMBean_name, "createMemoryPoolMBean") \ template(createMemoryManagerMBean_name, "createMemoryManagerMBean") \ template(createGarbageCollectorMBean_name, "createGarbageCollectorMBean") \ @@ -488,6 +495,7 @@ template(java_lang_management_MemoryPoolMXBean, "java/lang/management/MemoryPoolMXBean") \ template(java_lang_management_MemoryManagerMXBean, "java/lang/management/MemoryManagerMXBean") \ template(java_lang_management_GarbageCollectorMXBean,"java/lang/management/GarbageCollectorMXBean") \ + template(gcInfoBuilder_name, "gcInfoBuilder") \ template(createMemoryPool_name, "createMemoryPool") \ template(createMemoryManager_name, "createMemoryManager") \ template(createGarbageCollector_name, "createGarbageCollector") \ diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index bd49b0f9fc6..1b309fefc00 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -2026,7 +2026,7 @@ void CMSCollector::do_compaction_work(bool clear_all_soft_refs) { } { - TraceCMSMemoryManagerStats(); + TraceCMSMemoryManagerStats tmms(gch->gc_cause()); } GenMarkSweep::invoke_at_safepoint(_cmsGen->level(), ref_processor(), clear_all_soft_refs); @@ -3479,7 +3479,7 @@ CMSPhaseAccounting::~CMSPhaseAccounting() { void CMSCollector::checkpointRootsInitial(bool asynch) { assert(_collectorState == InitialMarking, "Wrong collector state"); check_correct_thread_executing(); - TraceCMSMemoryManagerStats tms(_collectorState); + TraceCMSMemoryManagerStats tms(_collectorState,GenCollectedHeap::heap()->gc_cause()); ReferenceProcessor* rp = ref_processor(); SpecializationStats::clear(); @@ -4858,7 +4858,8 @@ void CMSCollector::checkpointRootsFinal(bool asynch, // world is stopped at this checkpoint assert(SafepointSynchronize::is_at_safepoint(), "world should be stopped"); - TraceCMSMemoryManagerStats tms(_collectorState); + TraceCMSMemoryManagerStats tms(_collectorState,GenCollectedHeap::heap()->gc_cause()); + verify_work_stacks_empty(); verify_overflow_empty(); @@ -5993,7 +5994,7 @@ void CMSCollector::sweep(bool asynch) { verify_work_stacks_empty(); verify_overflow_empty(); increment_sweep_count(); - TraceCMSMemoryManagerStats tms(_collectorState); + TraceCMSMemoryManagerStats tms(_collectorState,GenCollectedHeap::heap()->gc_cause()); _inter_sweep_timer.stop(); _inter_sweep_estimate.sample(_inter_sweep_timer.seconds()); @@ -9235,11 +9236,12 @@ size_t MarkDeadObjectsClosure::do_blk(HeapWord* addr) { return res; } -TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase): TraceMemoryManagerStats() { +TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause): TraceMemoryManagerStats() { switch (phase) { case CMSCollector::InitialMarking: initialize(true /* fullGC */ , + cause /* cause of the GC */, true /* recordGCBeginTime */, true /* recordPreGCUsage */, false /* recordPeakUsage */, @@ -9251,6 +9253,7 @@ TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorSt case CMSCollector::FinalMarking: initialize(true /* fullGC */ , + cause /* cause of the GC */, false /* recordGCBeginTime */, false /* recordPreGCUsage */, false /* recordPeakUsage */, @@ -9262,6 +9265,7 @@ TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorSt case CMSCollector::Sweeping: initialize(true /* fullGC */ , + cause /* cause of the GC */, false /* recordGCBeginTime */, false /* recordPreGCUsage */, true /* recordPeakUsage */, @@ -9277,8 +9281,9 @@ TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorSt } // when bailing out of cms in concurrent mode failure -TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(): TraceMemoryManagerStats() { +TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(GCCause::Cause cause): TraceMemoryManagerStats() { initialize(true /* fullGC */ , + cause /* cause of the GC */, true /* recordGCBeginTime */, true /* recordPreGCUsage */, true /* recordPeakUsage */, diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp index 1838fd73951..d3e5d550db8 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp @@ -1895,8 +1895,8 @@ public: class TraceCMSMemoryManagerStats : public TraceMemoryManagerStats { public: - TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase); - TraceCMSMemoryManagerStats(); + TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause); + TraceCMSMemoryManagerStats(GCCause::Cause cause); }; diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index a0491af72cf..af6c28fa7e1 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -1162,7 +1162,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, PrintGC, true, gclog_or_tty); TraceCollectorStats tcs(g1mm()->full_collection_counters()); - TraceMemoryManagerStats tms(true /* fullGC */); + TraceMemoryManagerStats tms(true /* fullGC */, gc_cause()); double start = os::elapsedTime(); g1_policy()->record_full_collection_start(); @@ -3202,7 +3202,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { TraceTime t(verbose_str, PrintGC && !PrintGCDetails, true, gclog_or_tty); TraceCollectorStats tcs(g1mm()->incremental_collection_counters()); - TraceMemoryManagerStats tms(false /* fullGC */); + TraceMemoryManagerStats tms(false /* fullGC */, gc_cause()); // If the secondary_free_list is not empty, append it to the // free_list. No need to wait for the cleanup operation to finish; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp index 3d657b3dece..f5414723335 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp @@ -173,7 +173,7 @@ void PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) { TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); TraceTime t1(gc_cause_str, PrintGC, !PrintGCDetails, gclog_or_tty); TraceCollectorStats tcs(counters()); - TraceMemoryManagerStats tms(true /* Full GC */); + TraceMemoryManagerStats tms(true /* Full GC */,gc_cause); if (TraceGen1Time) accumulated_time()->start(); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp index 7ff75bd8d34..08b723c7c03 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp @@ -2057,7 +2057,7 @@ void PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) { TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); TraceTime t1(gc_cause_str, PrintGC, !PrintGCDetails, gclog_or_tty); TraceCollectorStats tcs(counters()); - TraceMemoryManagerStats tms(true /* Full GC */); + TraceMemoryManagerStats tms(true /* Full GC */,gc_cause); if (TraceGen1Time) accumulated_time()->start(); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp index 419ed6ad7bb..b2234676f1e 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp @@ -322,7 +322,7 @@ bool PSScavenge::invoke_no_policy() { TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); TraceTime t1("GC", PrintGC, !PrintGCDetails, gclog_or_tty); TraceCollectorStats tcs(counters()); - TraceMemoryManagerStats tms(false /* not full GC */); + TraceMemoryManagerStats tms(false /* not full GC */,gc_cause); if (TraceGen0Time) accumulated_time()->start(); diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp index 2165c21dfa7..4326db2b3d0 100644 --- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp +++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp @@ -537,7 +537,7 @@ void GenCollectedHeap::do_collection(bool full, // Timer for individual generations. Last argument is false: no CR TraceTime t1(_gens[i]->short_name(), PrintGCDetails, false, gclog_or_tty); TraceCollectorStats tcs(_gens[i]->counters()); - TraceMemoryManagerStats tmms(_gens[i]->kind()); + TraceMemoryManagerStats tmms(_gens[i]->kind(),gc_cause()); size_t prev_used = _gens[i]->used(); _gens[i]->stat_record()->invocations++; diff --git a/hotspot/src/share/vm/runtime/serviceThread.cpp b/hotspot/src/share/vm/runtime/serviceThread.cpp index e5c70f7b70c..3c121e96219 100644 --- a/hotspot/src/share/vm/runtime/serviceThread.cpp +++ b/hotspot/src/share/vm/runtime/serviceThread.cpp @@ -28,6 +28,7 @@ #include "runtime/serviceThread.hpp" #include "runtime/mutexLocker.hpp" #include "prims/jvmtiImpl.hpp" +#include "services/gcNotifier.hpp" ServiceThread* ServiceThread::_instance = NULL; @@ -81,6 +82,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { while (true) { bool sensors_changed = false; bool has_jvmti_events = false; + bool has_gc_notification_event = false; JvmtiDeferredEvent jvmti_event; { // Need state transition ThreadBlockInVM so that this thread @@ -95,9 +97,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); while (!(sensors_changed = LowMemoryDetector::has_pending_requests()) && - !(has_jvmti_events = JvmtiDeferredEventQueue::has_events())) { + !(has_jvmti_events = JvmtiDeferredEventQueue::has_events()) && + !(has_gc_notification_event = GCNotifier::has_event())) { // wait until one of the sensors has pending requests, or there is a - // pending JVMTI event to post + // pending JVMTI event or JMX GC notification to post Service_lock->wait(Mutex::_no_safepoint_check_flag); } @@ -113,6 +116,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { if (sensors_changed) { LowMemoryDetector::process_sensor_changes(jt); } + + if(has_gc_notification_event) { + GCNotifier::sendNotification(CHECK); + } } } diff --git a/hotspot/src/share/vm/services/gcNotifier.cpp b/hotspot/src/share/vm/services/gcNotifier.cpp new file mode 100644 index 00000000000..3f07668b31a --- /dev/null +++ b/hotspot/src/share/vm/services/gcNotifier.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2011, 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/vmSymbols.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/mutex.hpp" +#include "runtime/mutexLocker.hpp" +#include "services/gcNotifier.hpp" +#include "services/management.hpp" +#include "services/memoryService.hpp" +#include "memoryManager.hpp" +#include "memory/oopFactory.hpp" + +GCNotificationRequest *GCNotifier::first_request = NULL; +GCNotificationRequest *GCNotifier::last_request = NULL; + +void GCNotifier::pushNotification(GCMemoryManager *mgr, const char *action, const char *cause) { + // Make a copy of the last GC statistics + // GC may occur between now and the creation of the notification + int num_pools = MemoryService::num_memory_pools(); + GCStatInfo* stat = new GCStatInfo(num_pools); + mgr->get_last_gc_stat(stat); + GCNotificationRequest *request = new GCNotificationRequest(os::javaTimeMillis(),mgr,action,cause,stat); + addRequest(request); + } + +void GCNotifier::addRequest(GCNotificationRequest *request) { + MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); + if(first_request == NULL) { + first_request = request; + } else { + last_request->next = request; + } + last_request = request; + Service_lock->notify_all(); +} + +GCNotificationRequest *GCNotifier::getRequest() { + MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); + GCNotificationRequest *request = first_request; + if(first_request != NULL) { + first_request = first_request->next; + } + return request; +} + +bool GCNotifier::has_event() { + return first_request != NULL; +} + +static Handle getGcInfoBuilder(GCMemoryManager *gcManager,TRAPS) { + + klassOop k = Management::sun_management_GarbageCollectorImpl_klass(CHECK_NH); + instanceKlassHandle gcMBeanKlass (THREAD, k); + + instanceOop i = gcManager->get_memory_manager_instance(THREAD); + instanceHandle ih(THREAD, i); + + JavaValue result(T_OBJECT); + JavaCallArguments args(ih); + + JavaCalls::call_virtual(&result, + gcMBeanKlass, + vmSymbols::getGcInfoBuilder_name(), + vmSymbols::getGcInfoBuilder_signature(), + &args, + CHECK_NH); + return Handle(THREAD,(oop)result.get_jobject()); + +} + +static Handle createGcInfo(GCMemoryManager *gcManager, GCStatInfo *gcStatInfo,TRAPS) { + + // Fill the arrays of MemoryUsage objects with before and after GC + // per pool memory usage + + klassOop muKlass = Management::java_lang_management_MemoryUsage_klass(CHECK_NH); objArrayOop bu = oopFactory::new_objArray( muKlass,MemoryService::num_memory_pools(), CHECK_NH); + objArrayHandle usage_before_gc_ah(THREAD, bu); + objArrayOop au = oopFactory::new_objArray(muKlass,MemoryService::num_memory_pools(), CHECK_NH); + objArrayHandle usage_after_gc_ah(THREAD, au); + + for (int i = 0; i < MemoryService::num_memory_pools(); i++) { + Handle before_usage = MemoryService::create_MemoryUsage_obj(gcStatInfo->before_gc_usage_for_pool(i), CHECK_NH); + Handle after_usage; + + MemoryUsage u = gcStatInfo->after_gc_usage_for_pool(i); + if (u.max_size() == 0 && u.used() > 0) { + // If max size == 0, this pool is a survivor space. + // Set max size = -1 since the pools will be swapped after GC. + MemoryUsage usage(u.init_size(), u.used(), u.committed(), (size_t)-1); + after_usage = MemoryService::create_MemoryUsage_obj(usage, CHECK_NH); + } else { + after_usage = MemoryService::create_MemoryUsage_obj(u, CHECK_NH); + } + usage_before_gc_ah->obj_at_put(i, before_usage()); + usage_after_gc_ah->obj_at_put(i, after_usage()); + } + + // Current implementation only has 1 attribute (number of GC threads) + // The type is 'I' + objArrayOop extra_args_array = oopFactory::new_objArray(SystemDictionary::Integer_klass(), 1, CHECK_NH); + objArrayHandle extra_array (THREAD, extra_args_array); + klassOop itKlass= SystemDictionary::Integer_klass(); + instanceKlassHandle intK(THREAD, itKlass); + + instanceHandle extra_arg_val = intK->allocate_instance_handle(CHECK_NH); + + { + JavaValue res(T_VOID); + JavaCallArguments argsInt; + argsInt.push_oop(extra_arg_val); + argsInt.push_int(gcManager->num_gc_threads()); + + JavaCalls::call_special(&res, + intK, + vmSymbols::object_initializer_name(), + vmSymbols::int_void_signature(), + &argsInt, + CHECK_NH); + } + extra_array->obj_at_put(0,extra_arg_val()); + + klassOop gcInfoklass = Management::com_sun_management_GcInfo_klass(CHECK_NH); + instanceKlassHandle ik (THREAD,gcInfoklass); + + Handle gcInfo_instance = ik->allocate_instance_handle(CHECK_NH); + + JavaValue constructor_result(T_VOID); + JavaCallArguments constructor_args(16); + constructor_args.push_oop(gcInfo_instance); + constructor_args.push_oop(getGcInfoBuilder(gcManager,THREAD)); + constructor_args.push_long(gcStatInfo->gc_index()); + constructor_args.push_long(gcStatInfo->start_time()); + constructor_args.push_long(gcStatInfo->end_time()); + constructor_args.push_oop(usage_before_gc_ah); + constructor_args.push_oop(usage_after_gc_ah); + constructor_args.push_oop(extra_array); + + JavaCalls::call_special(&constructor_result, + ik, + vmSymbols::object_initializer_name(), + vmSymbols::com_sun_management_GcInfo_constructor_signature(), + &constructor_args, + CHECK_NH); + + return Handle(gcInfo_instance()); +} + +void GCNotifier::sendNotification(TRAPS) { + ResourceMark rm(THREAD); + GCNotificationRequest *request = getRequest(); + if(request != NULL) { + Handle objGcInfo = createGcInfo(request->gcManager,request->gcStatInfo,THREAD); + + Handle objName = java_lang_String::create_from_platform_dependent_str(request->gcManager->name(), CHECK); + Handle objAction = java_lang_String::create_from_platform_dependent_str(request->gcAction, CHECK); + Handle objCause = java_lang_String::create_from_platform_dependent_str(request->gcCause, CHECK); + + klassOop k = Management::sun_management_GarbageCollectorImpl_klass(CHECK); + instanceKlassHandle gc_mbean_klass (THREAD, k); + + instanceOop gc_mbean = request->gcManager->get_memory_manager_instance(THREAD); + instanceHandle gc_mbean_h(THREAD, gc_mbean); + if (!gc_mbean_h->is_a(k)) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "This GCMemoryManager doesn't have a GarbageCollectorMXBean"); + } + + JavaValue result(T_VOID); + JavaCallArguments args(gc_mbean_h); + args.push_long(request->timestamp); + args.push_oop(objName); + args.push_oop(objAction); + args.push_oop(objCause); + args.push_oop(objGcInfo); + + JavaCalls::call_virtual(&result, + gc_mbean_klass, + vmSymbols::createGCNotification_name(), + vmSymbols::createGCNotification_signature(), + &args, + CHECK); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + } + + delete request; + } +} + diff --git a/hotspot/src/share/vm/services/gcNotifier.hpp b/hotspot/src/share/vm/services/gcNotifier.hpp new file mode 100644 index 00000000000..7e0d8462e8f --- /dev/null +++ b/hotspot/src/share/vm/services/gcNotifier.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011, 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. + * + */ + +#ifndef SHARE_VM_SERVICES_GCNOTIFIER_HPP +#define SHARE_VM_SERVICES_GCNOTIFIER_HPP + +#include "memory/allocation.hpp" +#include "services/memoryPool.hpp" +#include "services/memoryService.hpp" +#include "services/memoryManager.hpp" + +class GCNotificationRequest : public CHeapObj { + friend class GCNotifier; + GCNotificationRequest *next; + jlong timestamp; + GCMemoryManager *gcManager; + const char *gcAction; + const char *gcCause; + GCStatInfo *gcStatInfo; +public: + GCNotificationRequest(jlong ts, GCMemoryManager *manager, const char*action, const char *cause,GCStatInfo *info) { + next = NULL; + timestamp = ts; + gcManager = manager; + gcAction = action; + gcCause = cause; + gcStatInfo = info; + } + + ~GCNotificationRequest() { + delete gcStatInfo; + } +}; + +class GCNotifier : public AllStatic { + friend class ServiceThread; +private: + static GCNotificationRequest *first_request; + static GCNotificationRequest *last_request; + static void addRequest(GCNotificationRequest *request); + static GCNotificationRequest *getRequest(); +public: + static void pushNotification(GCMemoryManager *manager, const char *action, const char *cause); + static bool has_event(); + static void sendNotification(TRAPS); +}; + +#endif // SHARE_VM_SERVICES_GCNOTIFIER_HPP diff --git a/hotspot/src/share/vm/services/jmm.h b/hotspot/src/share/vm/services/jmm.h index df08124e56c..d91d8784702 100644 --- a/hotspot/src/share/vm/services/jmm.h +++ b/hotspot/src/share/vm/services/jmm.h @@ -48,7 +48,7 @@ enum { JMM_VERSION_1_0 = 0x20010000, JMM_VERSION_1_1 = 0x20010100, // JDK 6 JMM_VERSION_1_2 = 0x20010200, // JDK 7 - JMM_VERSION = 0x20010200 + JMM_VERSION = 0x20010201 }; typedef struct { @@ -293,6 +293,9 @@ typedef struct jmmInterface_1_ { jlongArray ids, jboolean lockedMonitors, jboolean lockedSynchronizers); + void (JNICALL *SetGCNotificationEnabled) (JNIEnv *env, + jobject mgr, + jboolean enabled); } JmmInterface; #ifdef __cplusplus diff --git a/hotspot/src/share/vm/services/management.cpp b/hotspot/src/share/vm/services/management.cpp index 3c4f1481c75..9c8116c894e 100644 --- a/hotspot/src/share/vm/services/management.cpp +++ b/hotspot/src/share/vm/services/management.cpp @@ -42,6 +42,7 @@ #include "services/classLoadingService.hpp" #include "services/heapDumper.hpp" #include "services/lowMemoryDetector.hpp" +#include "services/gcNotifier.hpp" #include "services/management.hpp" #include "services/memoryManager.hpp" #include "services/memoryPool.hpp" @@ -60,6 +61,8 @@ klassOop Management::_memoryPoolMXBean_klass = NULL; klassOop Management::_memoryManagerMXBean_klass = NULL; klassOop Management::_garbageCollectorMXBean_klass = NULL; klassOop Management::_managementFactory_klass = NULL; +klassOop Management::_garbageCollectorImpl_klass = NULL; +klassOop Management::_gcInfo_klass = NULL; jmmOptionalSupport Management::_optional_support = {0}; TimeStamp Management::_stamp; @@ -179,6 +182,8 @@ void Management::oops_do(OopClosure* f) { f->do_oop((oop*) &_memoryManagerMXBean_klass); f->do_oop((oop*) &_garbageCollectorMXBean_klass); f->do_oop((oop*) &_managementFactory_klass); + f->do_oop((oop*) &_garbageCollectorImpl_klass); + f->do_oop((oop*) &_gcInfo_klass); } klassOop Management::java_lang_management_ThreadInfo_klass(TRAPS) { @@ -230,6 +235,20 @@ klassOop Management::sun_management_ManagementFactory_klass(TRAPS) { return _managementFactory_klass; } +klassOop Management::sun_management_GarbageCollectorImpl_klass(TRAPS) { + if (_garbageCollectorImpl_klass == NULL) { + _garbageCollectorImpl_klass = load_and_initialize_klass(vmSymbols::sun_management_GarbageCollectorImpl(), CHECK_NULL); + } + return _garbageCollectorImpl_klass; +} + +klassOop Management::com_sun_management_GcInfo_klass(TRAPS) { + if (_gcInfo_klass == NULL) { + _gcInfo_klass = load_and_initialize_klass(vmSymbols::com_sun_management_GcInfo(), CHECK_NULL); + } + return _gcInfo_klass; +} + static void initialize_ThreadInfo_constructor_arguments(JavaCallArguments* args, ThreadSnapshot* snapshot, TRAPS) { Handle snapshot_thread(THREAD, snapshot->threadObj()); @@ -2056,6 +2075,13 @@ JVM_ENTRY(void, jmm_GetLastGCStat(JNIEnv *env, jobject obj, jmmGCStat *gc_stat)) } JVM_END +JVM_ENTRY(void, jmm_SetGCNotificationEnabled(JNIEnv *env, jobject obj, jboolean enabled)) + ResourceMark rm(THREAD); + // Get the GCMemoryManager + GCMemoryManager* mgr = get_gc_memory_manager_from_jobject(obj, CHECK); + mgr->set_notification_enabled(enabled?true:false); +JVM_END + // Dump heap - Returns 0 if succeeds. JVM_ENTRY(jint, jmm_DumpHeap0(JNIEnv *env, jstring outputfile, jboolean live)) #ifndef SERVICES_KERNEL @@ -2122,7 +2148,8 @@ const struct jmmInterface_1_ jmm_interface = { jmm_FindDeadlockedThreads, jmm_SetVMGlobal, NULL, - jmm_DumpThreads + jmm_DumpThreads, + jmm_SetGCNotificationEnabled }; void* Management::get_jmm_interface(int version) { diff --git a/hotspot/src/share/vm/services/management.hpp b/hotspot/src/share/vm/services/management.hpp index 1598f2261e4..9d97988df9c 100644 --- a/hotspot/src/share/vm/services/management.hpp +++ b/hotspot/src/share/vm/services/management.hpp @@ -49,6 +49,8 @@ private: static klassOop _memoryManagerMXBean_klass; static klassOop _garbageCollectorMXBean_klass; static klassOop _managementFactory_klass; + static klassOop _garbageCollectorImpl_klass; + static klassOop _gcInfo_klass; static klassOop load_and_initialize_klass(Symbol* sh, TRAPS); @@ -86,6 +88,8 @@ public: static klassOop java_lang_management_GarbageCollectorMXBean_klass(TRAPS); static klassOop sun_management_Sensor_klass(TRAPS); static klassOop sun_management_ManagementFactory_klass(TRAPS); + static klassOop sun_management_GarbageCollectorImpl_klass(TRAPS); + static klassOop com_sun_management_GcInfo_klass(TRAPS); static instanceOop create_thread_info_instance(ThreadSnapshot* snapshot, TRAPS); static instanceOop create_thread_info_instance(ThreadSnapshot* snapshot, objArrayHandle monitors_array, typeArrayHandle depths_array, objArrayHandle synchronizers_array, TRAPS); diff --git a/hotspot/src/share/vm/services/memoryManager.cpp b/hotspot/src/share/vm/services/memoryManager.cpp index e13b6437645..3977a4562be 100644 --- a/hotspot/src/share/vm/services/memoryManager.cpp +++ b/hotspot/src/share/vm/services/memoryManager.cpp @@ -33,6 +33,7 @@ #include "services/memoryManager.hpp" #include "services/memoryPool.hpp" #include "services/memoryService.hpp" +#include "services/gcNotifier.hpp" #include "utilities/dtrace.hpp" HS_DTRACE_PROBE_DECL8(hotspot, mem__pool__gc__begin, char*, int, char*, int, @@ -202,6 +203,7 @@ GCMemoryManager::GCMemoryManager() : MemoryManager() { _last_gc_lock = new Mutex(Mutex::leaf, "_last_gc_lock", true); _current_gc_stat = NULL; _num_gc_threads = 1; + _notification_enabled = false; } GCMemoryManager::~GCMemoryManager() { @@ -250,7 +252,8 @@ void GCMemoryManager::gc_begin(bool recordGCBeginTime, bool recordPreGCUsage, // to ensure the current gc stat is placed in _last_gc_stat. void GCMemoryManager::gc_end(bool recordPostGCUsage, bool recordAccumulatedGCTime, - bool recordGCEndTime, bool countCollection) { + bool recordGCEndTime, bool countCollection, + GCCause::Cause cause) { if (recordAccumulatedGCTime) { _accumulated_timer.stop(); } @@ -283,6 +286,11 @@ void GCMemoryManager::gc_end(bool recordPostGCUsage, pool->set_last_collection_usage(usage); LowMemoryDetector::detect_after_gc_memory(pool); } + if(is_notification_enabled()) { + bool isMajorGC = this == MemoryService::get_major_gc_manager(); + GCNotifier::pushNotification(this, isMajorGC ? "end of major GC" : "end of minor GC", + GCCause::to_string(cause)); + } } if (countCollection) { _num_collections++; diff --git a/hotspot/src/share/vm/services/memoryManager.hpp b/hotspot/src/share/vm/services/memoryManager.hpp index 4a3eadb54d8..cd4d953bfe2 100644 --- a/hotspot/src/share/vm/services/memoryManager.hpp +++ b/hotspot/src/share/vm/services/memoryManager.hpp @@ -166,6 +166,7 @@ private: Mutex* _last_gc_lock; GCStatInfo* _current_gc_stat; int _num_gc_threads; + volatile bool _notification_enabled; public: GCMemoryManager(); ~GCMemoryManager(); @@ -181,7 +182,7 @@ public: void gc_begin(bool recordGCBeginTime, bool recordPreGCUsage, bool recordAccumulatedGCTime); void gc_end(bool recordPostGCUsage, bool recordAccumulatedGCTime, - bool recordGCEndTime, bool countCollection); + bool recordGCEndTime, bool countCollection, GCCause::Cause cause); void reset_gc_stat() { _num_collections = 0; _accumulated_timer.reset(); } @@ -189,6 +190,8 @@ public: // the collection count. Zero signifies no gc has taken place. size_t get_last_gc_stat(GCStatInfo* dest); + void set_notification_enabled(bool enabled) { _notification_enabled = enabled; } + bool is_notification_enabled() { return _notification_enabled; } virtual MemoryManager::Name kind() = 0; }; diff --git a/hotspot/src/share/vm/services/memoryService.cpp b/hotspot/src/share/vm/services/memoryService.cpp index eca4d6418b7..d1bb9c2d60e 100644 --- a/hotspot/src/share/vm/services/memoryService.cpp +++ b/hotspot/src/share/vm/services/memoryService.cpp @@ -565,7 +565,8 @@ void MemoryService::gc_begin(bool fullGC, bool recordGCBeginTime, void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage, bool recordAccumulatedGCTime, - bool recordGCEndTime, bool countCollection) { + bool recordGCEndTime, bool countCollection, + GCCause::Cause cause) { GCMemoryManager* mgr; if (fullGC) { @@ -577,7 +578,7 @@ void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage, // register the GC end statistics and memory usage mgr->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, - countCollection); + countCollection, cause); } void MemoryService::oops_do(OopClosure* f) { @@ -633,7 +634,7 @@ Handle MemoryService::create_MemoryUsage_obj(MemoryUsage usage, TRAPS) { // gc manager (so _fullGC is set to false ) and for other generation kinds // doing mark-sweep-compact uses major gc manager (so _fullGC is set // to true). -TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind) { +TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind, GCCause::Cause cause) { switch (kind) { case Generation::DefNew: #ifndef SERIALGC @@ -654,9 +655,10 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind) { } // this has to be called in a stop the world pause and represent // an entire gc pause, start to finish: - initialize(_fullGC, true, true, true, true, true, true, true); + initialize(_fullGC, cause,true, true, true, true, true, true, true); } TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC, + GCCause::Cause cause, bool recordGCBeginTime, bool recordPreGCUsage, bool recordPeakUsage, @@ -664,7 +666,7 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection) { - initialize(fullGC, recordGCBeginTime, recordPreGCUsage, recordPeakUsage, + initialize(fullGC, cause, recordGCBeginTime, recordPreGCUsage, recordPeakUsage, recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, countCollection); } @@ -672,6 +674,7 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC, // for a subclass to create then initialize an instance before invoking // the MemoryService void TraceMemoryManagerStats::initialize(bool fullGC, + GCCause::Cause cause, bool recordGCBeginTime, bool recordPreGCUsage, bool recordPeakUsage, @@ -687,6 +690,7 @@ void TraceMemoryManagerStats::initialize(bool fullGC, _recordAccumulatedGCTime = recordAccumulatedGCTime; _recordGCEndTime = recordGCEndTime; _countCollection = countCollection; + _cause = cause; MemoryService::gc_begin(_fullGC, _recordGCBeginTime, _recordAccumulatedGCTime, _recordPreGCUsage, _recordPeakUsage); @@ -694,6 +698,6 @@ void TraceMemoryManagerStats::initialize(bool fullGC, TraceMemoryManagerStats::~TraceMemoryManagerStats() { MemoryService::gc_end(_fullGC, _recordPostGCUsage, _recordAccumulatedGCTime, - _recordGCEndTime, _countCollection); + _recordGCEndTime, _countCollection, _cause); } diff --git a/hotspot/src/share/vm/services/memoryService.hpp b/hotspot/src/share/vm/services/memoryService.hpp index 295316b5f78..2200d214b7c 100644 --- a/hotspot/src/share/vm/services/memoryService.hpp +++ b/hotspot/src/share/vm/services/memoryService.hpp @@ -29,6 +29,7 @@ #include "memory/generation.hpp" #include "runtime/handles.hpp" #include "services/memoryUsage.hpp" +#include "gc_interface/gcCause.hpp" // Forward declaration class MemoryPool; @@ -162,7 +163,8 @@ public: bool recordPreGCUsage, bool recordPeakUsage); static void gc_end(bool fullGC, bool recordPostGCUsage, bool recordAccumulatedGCTime, - bool recordGCEndTime, bool countCollection); + bool recordGCEndTime, bool countCollection, + GCCause::Cause cause); static void oops_do(OopClosure* f); @@ -172,6 +174,14 @@ public: // Create an instance of java/lang/management/MemoryUsage static Handle create_MemoryUsage_obj(MemoryUsage usage, TRAPS); + + static const GCMemoryManager* get_minor_gc_manager() { + return _minor_gc_manager; + } + + static const GCMemoryManager* get_major_gc_manager() { + return _major_gc_manager; + } }; class TraceMemoryManagerStats : public StackObj { @@ -184,10 +194,11 @@ private: bool _recordAccumulatedGCTime; bool _recordGCEndTime; bool _countCollection; - + GCCause::Cause _cause; public: TraceMemoryManagerStats() {} TraceMemoryManagerStats(bool fullGC, + GCCause::Cause cause, bool recordGCBeginTime = true, bool recordPreGCUsage = true, bool recordPeakUsage = true, @@ -197,6 +208,7 @@ public: bool countCollection = true); void initialize(bool fullGC, + GCCause::Cause cause, bool recordGCBeginTime, bool recordPreGCUsage, bool recordPeakUsage, @@ -205,7 +217,7 @@ public: bool recordGCEndTime, bool countCollection); - TraceMemoryManagerStats(Generation::Name kind); + TraceMemoryManagerStats(Generation::Name kind, GCCause::Cause cause); ~TraceMemoryManagerStats(); }; From db9005f0d38775e0b97520fec0ed8a0ecb6b418a Mon Sep 17 00:00:00 2001 From: Christian Thalinger Date: Thu, 12 May 2011 14:04:48 -0700 Subject: [PATCH 43/65] 6998541: JSR 292 implement missing return-type conversion for OP_RETYPE_RAW Reviewed-by: jrose, kvn, never --- .../src/cpu/sparc/vm/methodHandles_sparc.cpp | 20 +- hotspot/src/cpu/x86/vm/methodHandles_x86.cpp | 59 ++-- hotspot/src/share/vm/ci/ciMethodData.hpp | 7 +- hotspot/src/share/vm/ci/ciMethodHandle.cpp | 21 +- hotspot/src/share/vm/ci/ciMethodHandle.hpp | 27 +- hotspot/src/share/vm/opto/doCall.cpp | 18 +- .../src/share/vm/prims/methodHandleWalk.cpp | 286 ++++++++++++------ .../src/share/vm/prims/methodHandleWalk.hpp | 28 +- hotspot/src/share/vm/prims/methodHandles.cpp | 84 +++-- hotspot/src/share/vm/prims/methodHandles.hpp | 2 + 10 files changed, 355 insertions(+), 197 deletions(-) diff --git a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp index 4e80e1acdda..5f858bd84ee 100644 --- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp @@ -142,18 +142,8 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* Register O2_form = O2_scratch; Register O3_adapter = O3_scratch; __ load_heap_oop(Address(O0_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, O1_scratch)), O2_form); - // load_heap_oop(Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter); - // deal with old JDK versions: - __ add( Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter); - __ cmp(O3_adapter, O2_form); - Label sorry_no_invoke_generic; - __ brx(Assembler::lessUnsigned, false, Assembler::pn, sorry_no_invoke_generic); - __ delayed()->nop(); - - __ load_heap_oop(Address(O3_adapter, 0), O3_adapter); - __ tst(O3_adapter); - __ brx(Assembler::zero, false, Assembler::pn, sorry_no_invoke_generic); - __ delayed()->nop(); + __ load_heap_oop(Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter); + __ verify_oop(O3_adapter); __ st_ptr(O3_adapter, Address(O4_argbase, 1 * Interpreter::stackElementSize)); // As a trusted first argument, pass the type being called, so the adapter knows // the actual types of the arguments and return values. @@ -164,12 +154,6 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* trace_method_handle(_masm, "invokeGeneric"); __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); - __ bind(sorry_no_invoke_generic); // no invokeGeneric implementation available! - __ mov(O0_mtype, G5_method_type); // required by throw_WrongMethodType - // mov(G3_method_handle, G3_method_handle); // already in this register - __ jump_to(AddressLiteral(Interpreter::throw_WrongMethodType_entry()), O1_scratch); - __ delayed()->nop(); - return entry_point; } diff --git a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp index 8f73dc13d8d..1427d13c2c4 100644 --- a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp +++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp @@ -163,7 +163,7 @@ void MethodHandles::RicochetFrame::generate_ricochet_blob(MacroAssembler* _masm, BLOCK_COMMENT("ricochet_blob.bounce"); if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm); - trace_method_handle(_masm, "ricochet_blob.bounce"); + trace_method_handle(_masm, "return/ricochet_blob.bounce"); __ jmp(frame_address(continuation_offset_in_bytes())); __ hlt(); @@ -615,18 +615,10 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* rcx_argslot, rbx_temp, rdx_temp); // load up an adapter from the calling type (Java weaves this) - __ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp))); Register rdx_adapter = rdx_temp; - // __ load_heap_oop(rdx_adapter, Address(rdx_temp, java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes())); - // deal with old JDK versions: - __ lea(rdi_temp, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, rdi_temp))); - __ cmpptr(rdi_temp, rdx_temp); - Label sorry_no_invoke_generic; - __ jcc(Assembler::below, sorry_no_invoke_generic); - - __ load_heap_oop(rdx_adapter, Address(rdi_temp, 0)); - __ testptr(rdx_adapter, rdx_adapter); - __ jcc(Assembler::zero, sorry_no_invoke_generic); + __ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp))); + __ load_heap_oop(rdx_adapter, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, rdi_temp))); + __ verify_oop(rdx_adapter); __ movptr(Address(rcx_argslot, 1 * Interpreter::stackElementSize), rdx_adapter); // As a trusted first argument, pass the type being called, so the adapter knows // the actual types of the arguments and return values. @@ -637,12 +629,6 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* trace_method_handle(_masm, "invokeGeneric"); __ jump_to_method_handle_entry(rcx, rdi_temp); - __ bind(sorry_no_invoke_generic); // no invokeGeneric implementation available! - __ movptr(rcx_recv, Address(rcx_argslot, -1 * Interpreter::stackElementSize)); // recover original MH - __ push(rax_mtype); // required mtype - __ push(rcx_recv); // bad mh (1st stacked argument) - __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry())); - return entry_point; } @@ -688,7 +674,7 @@ void MethodHandles::insert_arg_slots(MacroAssembler* _masm, __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp); __ addptr(rdx_temp, wordSize); __ cmpptr(rdx_temp, rax_argslot); - __ jcc(Assembler::less, loop); + __ jcc(Assembler::below, loop); } // Now move the argslot down, to point to the opened-up space. @@ -731,7 +717,7 @@ void MethodHandles::remove_arg_slots(MacroAssembler* _masm, __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp); __ addptr(rdx_temp, -wordSize); __ cmpptr(rdx_temp, rsp); - __ jcc(Assembler::greaterEqual, loop); + __ jcc(Assembler::aboveEqual, loop); } // Now move the argslot up, to point to the just-copied block. @@ -980,12 +966,21 @@ void trace_method_handle_stub(const char* adaptername, intptr_t* saved_sp, intptr_t* saved_bp) { // called as a leaf from native code: do not block the JVM! + bool has_mh = (strstr(adaptername, "return/") == NULL); // return adapters don't have rcx_mh intptr_t* last_sp = (intptr_t*) saved_bp[frame::interpreter_frame_last_sp_offset]; - intptr_t* base_sp = (intptr_t*) saved_bp[frame::interpreter_frame_monitor_block_top_offset]; - tty->print_cr("MH %s mh="INTPTR_FORMAT" sp=("INTPTR_FORMAT"+"INTX_FORMAT") stack_size="INTX_FORMAT" bp="INTPTR_FORMAT, - adaptername, (intptr_t)mh, (intptr_t)entry_sp, (intptr_t)(saved_sp - entry_sp), (intptr_t)(base_sp - last_sp), (intptr_t)saved_bp); - if (last_sp != saved_sp && last_sp != NULL) - tty->print_cr("*** last_sp="INTPTR_FORMAT, (intptr_t)last_sp); + intptr_t* base_sp = last_sp; + typedef MethodHandles::RicochetFrame RicochetFrame; + RicochetFrame* rfp = (RicochetFrame*)((address)saved_bp - RicochetFrame::sender_link_offset_in_bytes()); + if (!UseRicochetFrames || Universe::heap()->is_in((address) rfp->saved_args_base())) { + // Probably an interpreter frame. + base_sp = (intptr_t*) saved_bp[frame::interpreter_frame_monitor_block_top_offset]; + } + intptr_t mh_reg = (intptr_t)mh; + const char* mh_reg_name = "rcx_mh"; + if (!has_mh) mh_reg_name = "rcx"; + tty->print_cr("MH %s %s="PTR_FORMAT" sp=("PTR_FORMAT"+"INTX_FORMAT") stack_size="INTX_FORMAT" bp="PTR_FORMAT, + adaptername, mh_reg_name, mh_reg, + (intptr_t)entry_sp, (intptr_t)(saved_sp - entry_sp), (intptr_t)(base_sp - last_sp), (intptr_t)saved_bp); if (Verbose) { tty->print(" reg dump: "); int saved_regs_count = (entry_sp-1) - saved_regs; @@ -996,18 +991,21 @@ void trace_method_handle_stub(const char* adaptername, tty->cr(); tty->print(" + dump: "); } - tty->print(" %d: "INTPTR_FORMAT, i, saved_regs[i]); + tty->print(" %d: "PTR_FORMAT, i, saved_regs[i]); } tty->cr(); + if (last_sp != saved_sp && last_sp != NULL) + tty->print_cr("*** last_sp="PTR_FORMAT, (intptr_t)last_sp); int stack_dump_count = 16; if (stack_dump_count < (int)(saved_bp + 2 - saved_sp)) stack_dump_count = (int)(saved_bp + 2 - saved_sp); if (stack_dump_count > 64) stack_dump_count = 48; for (i = 0; i < stack_dump_count; i += 4) { - tty->print_cr(" dump at SP[%d] "INTPTR_FORMAT": "INTPTR_FORMAT" "INTPTR_FORMAT" "INTPTR_FORMAT" "INTPTR_FORMAT, + tty->print_cr(" dump at SP[%d] "PTR_FORMAT": "PTR_FORMAT" "PTR_FORMAT" "PTR_FORMAT" "PTR_FORMAT, i, (intptr_t) &entry_sp[i+0], entry_sp[i+0], entry_sp[i+1], entry_sp[i+2], entry_sp[i+3]); } - print_method_handle(mh); + if (has_mh) + print_method_handle(mh); } } @@ -1073,7 +1071,6 @@ int MethodHandles::adapter_conversion_ops_supported_mask() { //OP_COLLECT_ARGS is below... |(1<get_methodOop()); // We catch all exceptions here that could happen in the method // handle compiler and stop the VM. - MethodHandleCompiler mhc(h, callee, call_profile()->count(), is_invokedynamic, THREAD); + MethodHandleCompiler mhc(h, callee, _profile->count(), is_invokedynamic, THREAD); if (!HAS_PENDING_EXCEPTION) { methodHandle m = mhc.compile(THREAD); if (!HAS_PENDING_EXCEPTION) { @@ -58,6 +59,22 @@ ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const { return NULL; } +// ------------------------------------------------------------------ +// ciMethodHandle::get_adapter +// +// Return an adapter for this MethodHandle. +ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const { + ciMethod* result = get_adapter_impl(is_invokedynamic); + if (result) { + // Fake up the MDO maturity. + ciMethodData* mdo = result->method_data(); + if (mdo != NULL && _caller->method_data() != NULL && _caller->method_data()->is_mature()) { + mdo->set_mature(); + } + } + return result; +} + // ------------------------------------------------------------------ // ciMethodHandle::print_impl diff --git a/hotspot/src/share/vm/ci/ciMethodHandle.hpp b/hotspot/src/share/vm/ci/ciMethodHandle.hpp index 788d374b5f9..d9519d4a6e9 100644 --- a/hotspot/src/share/vm/ci/ciMethodHandle.hpp +++ b/hotspot/src/share/vm/ci/ciMethodHandle.hpp @@ -35,35 +35,36 @@ class ciMethodHandle : public ciInstance { private: ciMethod* _callee; + ciMethod* _caller; ciCallProfile* _profile; // Return an adapter for this MethodHandle. - ciMethod* get_adapter(bool is_invokedynamic) const; + ciMethod* get_adapter_impl(bool is_invokedynamic) const; + ciMethod* get_adapter( bool is_invokedynamic) const; protected: void print_impl(outputStream* st); public: - ciMethodHandle(instanceHandle h_i) : ciInstance(h_i) {}; + ciMethodHandle(instanceHandle h_i) : + ciInstance(h_i), + _callee(NULL), + _caller(NULL), + _profile(NULL) + {} // What kind of ciObject is this? bool is_method_handle() const { return true; } - ciMethod* callee() const { return _callee; } - void set_callee(ciMethod* m) { _callee = m; } - - ciCallProfile* call_profile() const { return _profile; } - void set_call_profile(ciCallProfile* profile) { _profile = profile; } + void set_callee(ciMethod* m) { _callee = m; } + void set_caller(ciMethod* m) { _caller = m; } + void set_call_profile(ciCallProfile* profile) { _profile = profile; } // Return an adapter for a MethodHandle call. - ciMethod* get_method_handle_adapter() const { - return get_adapter(false); - } + ciMethod* get_method_handle_adapter() const { return get_adapter(false); } // Return an adapter for an invokedynamic call. - ciMethod* get_invokedynamic_adapter() const { - return get_adapter(true); - } + ciMethod* get_invokedynamic_adapter() const { return get_adapter(true); } }; #endif // SHARE_VM_CI_CIMETHODHANDLE_HPP diff --git a/hotspot/src/share/vm/opto/doCall.cpp b/hotspot/src/share/vm/opto/doCall.cpp index 374d9b79db4..3c97b3d6a20 100644 --- a/hotspot/src/share/vm/opto/doCall.cpp +++ b/hotspot/src/share/vm/opto/doCall.cpp @@ -62,7 +62,10 @@ void trace_type_profile(ciMethod *method, int depth, int bci, ciMethod *prof_met CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual, JVMState* jvms, bool allow_inline, float prof_factor) { - CallGenerator* cg; + CallGenerator* cg; + ciMethod* caller = jvms->method(); + int bci = jvms->bci(); + Bytecodes::Code bytecode = caller->java_code_at_bci(bci); guarantee(call_method != NULL, "failed method resolution"); // Dtrace currently doesn't work unless all calls are vanilla @@ -73,8 +76,7 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, // Note: When we get profiling during stage-1 compiles, we want to pull // from more specific profile data which pertains to this inlining. // Right now, ignore the information in jvms->caller(), and do method[bci]. - ciCallProfile profile = jvms->method()->call_profile_at_bci(jvms->bci()); - Bytecodes::Code bytecode = jvms->method()->java_code_at_bci(jvms->bci()); + ciCallProfile profile = caller->call_profile_at_bci(bci); // See how many times this site has been invoked. int site_count = profile.count(); @@ -126,9 +128,10 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, ciObject* const_oop = oop_ptr->const_oop(); ciMethodHandle* method_handle = const_oop->as_method_handle(); - // Set the actually called method to have access to the class - // and signature in the MethodHandleCompiler. + // Set the callee to have access to the class and signature in + // the MethodHandleCompiler. method_handle->set_callee(call_method); + method_handle->set_caller(caller); method_handle->set_call_profile(&profile); // Get an adapter for the MethodHandle. @@ -150,9 +153,10 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, ciCallSite* call_site = str.get_call_site(); ciMethodHandle* method_handle = call_site->get_target(); - // Set the actually called method to have access to the class - // and signature in the MethodHandleCompiler. + // Set the callee to have access to the class and signature in + // the MethodHandleCompiler. method_handle->set_callee(call_method); + method_handle->set_caller(caller); method_handle->set_call_profile(&profile); // Get an adapter for the MethodHandle. diff --git a/hotspot/src/share/vm/prims/methodHandleWalk.cpp b/hotspot/src/share/vm/prims/methodHandleWalk.cpp index 1962fddca58..cc8f8b539df 100644 --- a/hotspot/src/share/vm/prims/methodHandleWalk.cpp +++ b/hotspot/src/share/vm/prims/methodHandleWalk.cpp @@ -31,6 +31,11 @@ * JSR 292 reference implementation: method handle structure analysis */ +#ifdef PRODUCT +#define print_method_handle(mh) {} +#else //PRODUCT +extern "C" void print_method_handle(oop mh); +#endif //PRODUCT // ----------------------------------------------------------------------------- // MethodHandleChain @@ -206,8 +211,10 @@ MethodHandleWalker::walk(TRAPS) { lose("bad argument index", CHECK_(empty)); } + bool retain_original_args = false; // used by fold/collect logic + // perform the adapter action - switch (chain().adapter_conversion_op()) { + switch (conv_op) { case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY: // No changes to arguments; pass the bits through. break; @@ -216,51 +223,36 @@ MethodHandleWalker::walk(TRAPS) { // To keep the verifier happy, emit bitwise ("raw") conversions as needed. // See MethodHandles::same_basic_type_for_arguments for allowed conversions. Handle incoming_mtype(THREAD, chain().method_type_oop()); - oop outgoing_mh_oop = chain().vmtarget_oop(); - if (!java_lang_invoke_MethodHandle::is_instance(outgoing_mh_oop)) - lose("outgoing target not a MethodHandle", CHECK_(empty)); - Handle outgoing_mtype(THREAD, java_lang_invoke_MethodHandle::type(outgoing_mh_oop)); - outgoing_mh_oop = NULL; // GC safety + Handle outgoing_mtype; + { + oop outgoing_mh_oop = chain().vmtarget_oop(); + if (!java_lang_invoke_MethodHandle::is_instance(outgoing_mh_oop)) + lose("outgoing target not a MethodHandle", CHECK_(empty)); + outgoing_mtype = Handle(THREAD, java_lang_invoke_MethodHandle::type(outgoing_mh_oop)); + } int nptypes = java_lang_invoke_MethodType::ptype_count(outgoing_mtype()); if (nptypes != java_lang_invoke_MethodType::ptype_count(incoming_mtype())) lose("incoming and outgoing parameter count do not agree", CHECK_(empty)); + // Argument types. for (int i = 0, slot = _outgoing.length() - 1; slot >= 0; slot--) { SlotState* arg_state = slot_state(slot); if (arg_state->_type == T_VOID) continue; - ArgToken arg = _outgoing.at(slot)._arg; - - klassOop in_klass = NULL; - klassOop out_klass = NULL; - BasicType inpbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(incoming_mtype(), i), &in_klass); - BasicType outpbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(outgoing_mtype(), i), &out_klass); - assert(inpbt == arg.basic_type(), "sanity"); - - if (inpbt != outpbt) { - vmIntrinsics::ID iid = vmIntrinsics::for_raw_conversion(inpbt, outpbt); - if (iid == vmIntrinsics::_none) { - lose("no raw conversion method", CHECK_(empty)); - } - ArgToken arglist[2]; - arglist[0] = arg; // outgoing 'this' - arglist[1] = ArgToken(); // sentinel - arg = make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty)); - change_argument(inpbt, slot, outpbt, arg); - } + klassOop src_klass = NULL; + klassOop dst_klass = NULL; + BasicType src = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(incoming_mtype(), i), &src_klass); + BasicType dst = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(outgoing_mtype(), i), &dst_klass); + retype_raw_argument_type(src, dst, slot, CHECK_(empty)); i++; // We need to skip void slots at the top of the loop. } - BasicType inrbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(incoming_mtype())); - BasicType outrbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(outgoing_mtype())); - if (inrbt != outrbt) { - if (inrbt == T_INT && outrbt == T_VOID) { - // See comments in MethodHandles::same_basic_type_for_arguments. - } else { - assert(false, "IMPLEMENT ME"); - lose("no raw conversion method", CHECK_(empty)); - } + // Return type. + { + BasicType src = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(incoming_mtype())); + BasicType dst = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(outgoing_mtype())); + retype_raw_return_type(src, dst, CHECK_(empty)); } break; } @@ -273,7 +265,7 @@ MethodHandleWalker::walk(TRAPS) { assert(dest == arg_state->_type, ""); ArgToken arg = arg_state->_arg; ArgToken new_arg = make_conversion(T_OBJECT, dest_klass, Bytecodes::_checkcast, arg, CHECK_(empty)); - assert(arg.index() == new_arg.index(), "should be the same index"); + assert(arg.token_type() >= tt_symbolic || arg.index() == new_arg.index(), "should be the same index"); debug_only(dest_klass = (klassOop)badOop); break; } @@ -332,7 +324,7 @@ MethodHandleWalker::walk(TRAPS) { ArgToken arglist[2]; arglist[0] = arg; // outgoing value arglist[1] = ArgToken(); // sentinel - arg = make_invoke(NULL, boxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_(empty)); + arg = make_invoke(NULL, boxer, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty)); change_argument(src, arg_slot, T_OBJECT, arg); break; } @@ -404,13 +396,54 @@ MethodHandleWalker::walk(TRAPS) { break; } - case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: { //NYI, may GC - lose("unimplemented", CHECK_(empty)); - break; - } - - case java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS: { //NYI, may GC - lose("unimplemented", CHECK_(empty)); + case java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS: + retain_original_args = true; // and fall through: + case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: { + // call argument MH recursively + //{static int x; if (!x++) print_method_handle(chain().method_handle_oop()); --x;} + Handle recursive_mh(THREAD, chain().adapter_arg_oop()); + if (!java_lang_invoke_MethodHandle::is_instance(recursive_mh())) { + lose("recursive target not a MethodHandle", CHECK_(empty)); + } + Handle recursive_mtype(THREAD, java_lang_invoke_MethodHandle::type(recursive_mh())); + int argc = java_lang_invoke_MethodType::ptype_count(recursive_mtype()); + int coll_slots = java_lang_invoke_MethodHandle::vmslots(recursive_mh()); + BasicType rtype = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(recursive_mtype())); + ArgToken* arglist = NEW_RESOURCE_ARRAY(ArgToken, 1 + argc + 1); // 1+: mh, +1: sentinel + arglist[0] = make_oop_constant(recursive_mh(), CHECK_(empty)); + if (arg_slot < 0 || coll_slots < 0 || arg_slot + coll_slots > _outgoing.length()) { + lose("bad fold/collect arg slot", CHECK_(empty)); + } + for (int i = 0, slot = arg_slot + coll_slots - 1; slot >= arg_slot; slot--) { + SlotState* arg_state = slot_state(slot); + BasicType arg_type = arg_state->_type; + if (arg_type == T_VOID) continue; + ArgToken arg = _outgoing.at(slot)._arg; + if (i >= argc) { lose("bad fold/collect arg", CHECK_(empty)); } + arglist[1+i] = arg; + if (!retain_original_args) + change_argument(arg_type, slot, T_VOID, ArgToken(tt_void)); + } + arglist[1+argc] = ArgToken(); // sentinel + oop invoker = java_lang_invoke_MethodTypeForm::vmlayout( + java_lang_invoke_MethodType::form(recursive_mtype()) ); + if (invoker == NULL || !invoker->is_method()) { + lose("bad vmlayout slot", CHECK_(empty)); + } + // FIXME: consider inlining the invokee at the bytecode level + ArgToken ret = make_invoke(methodOop(invoker), vmIntrinsics::_none, + Bytecodes::_invokevirtual, false, 1+argc, &arglist[0], CHECK_(empty)); + DEBUG_ONLY(invoker = NULL); + if (rtype == T_OBJECT) { + klassOop rklass = java_lang_Class::as_klassOop( java_lang_invoke_MethodType::rtype(recursive_mtype()) ); + if (rklass != SystemDictionary::Object_klass() && + !Klass::cast(rklass)->is_interface()) { + // preserve type safety + ret = make_conversion(T_OBJECT, rklass, Bytecodes::_checkcast, ret, CHECK_(empty)); + } + } + int ret_slot = arg_slot + (retain_original_args ? coll_slots : 0); + change_argument(T_VOID, ret_slot, rtype, ret); break; } @@ -504,7 +537,7 @@ MethodHandleWalker::walk(TRAPS) { lose("bad bound value", CHECK_(empty)); } } - debug_only(arg_oop = badOop); + DEBUG_ONLY(arg_oop = badOop); change_argument(T_VOID, arg_slot, arg_type, arg); } @@ -547,11 +580,10 @@ void MethodHandleWalker::walk_incoming_state(TRAPS) { } for (int i = 0; i < nptypes; i++) { klassOop arg_type_klass = NULL; - BasicType arg_type = java_lang_Class::as_BasicType( - java_lang_invoke_MethodType::ptype(mtype(), i), &arg_type_klass); + BasicType arg_type = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(mtype(), i), &arg_type_klass); int index = new_local_index(arg_type); ArgToken arg = make_parameter(arg_type, arg_type_klass, index, CHECK); - debug_only(arg_type_klass = (klassOop) NULL); + DEBUG_ONLY(arg_type_klass = (klassOop) NULL); _outgoing.at_put(argp, make_state(arg_type, arg)); if (type2size[arg_type] == 2) { // add the extra slot, so we can model the JVM stack @@ -561,8 +593,7 @@ void MethodHandleWalker::walk_incoming_state(TRAPS) { } // call make_parameter at the end of the list for the return type klassOop ret_type_klass = NULL; - BasicType ret_type = java_lang_Class::as_BasicType( - java_lang_invoke_MethodType::rtype(mtype()), &ret_type_klass); + BasicType ret_type = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(mtype()), &ret_type_klass); ArgToken ret = make_parameter(ret_type, ret_type_klass, -1, CHECK); // ignore ret; client can catch it if needed } @@ -613,6 +644,48 @@ int MethodHandleWalker::argument_count_slow() { #endif +// ----------------------------------------------------------------------------- +// MethodHandleWalker::retype_raw_conversion +// +// Do the raw retype conversions for OP_RETYPE_RAW. +void MethodHandleWalker::retype_raw_conversion(BasicType src, BasicType dst, bool for_return, int slot, TRAPS) { + if (src != dst) { + if (MethodHandles::same_basic_type_for_returns(src, dst, /*raw*/ true)) { + if (MethodHandles::is_float_fixed_reinterpretation_cast(src, dst)) { + if (for_return) Untested("MHW return raw conversion"); // still untested + vmIntrinsics::ID iid = vmIntrinsics::for_raw_conversion(src, dst); + if (iid == vmIntrinsics::_none) { + lose("no raw conversion method", CHECK); + } + ArgToken arglist[2]; + if (!for_return) { + // argument type conversion + ArgToken arg = _outgoing.at(slot)._arg; + assert(arg.token_type() >= tt_symbolic || src == arg.basic_type(), "sanity"); + arglist[0] = arg; // outgoing 'this' + arglist[1] = ArgToken(); // sentinel + arg = make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK); + change_argument(src, slot, dst, arg); + } else { + // return type conversion + klassOop arg_klass = NULL; + arglist[0] = make_parameter(src, arg_klass, -1, CHECK); // return value + arglist[1] = ArgToken(); // sentinel + (void) make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK); + } + } else { + // Nothing to do. + } + } else if (src == T_OBJECT && is_java_primitive(dst)) { + // ref-to-prim: discard ref, push zero + lose("requested ref-to-prim conversion not expected", CHECK); + } else { + lose("requested raw conversion not allowed", CHECK); + } + } +} + + // ----------------------------------------------------------------------------- // MethodHandleCompiler @@ -719,6 +792,7 @@ void MethodHandleCompiler::emit_bc(Bytecodes::Code op, int index) { case Bytecodes::_astore_1: case Bytecodes::_astore_2: case Bytecodes::_astore_3: + case Bytecodes::_iand: case Bytecodes::_i2l: case Bytecodes::_i2f: case Bytecodes::_i2d: @@ -945,7 +1019,11 @@ MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Co break; default: - ShouldNotReachHere(); + if (op == Bytecodes::_illegal) + lose("no such primitive conversion", THREAD); + else + lose("bad primitive conversion op", THREAD); + return make_prim_constant(type, &zero_jvalue, THREAD); } return make_parameter(type, tk, index, THREAD); @@ -956,7 +1034,9 @@ MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Co // MethodHandleCompiler // -static jvalue zero_jvalue; +// Values used by the compiler. +jvalue MethodHandleCompiler::zero_jvalue = { 0 }; +jvalue MethodHandleCompiler::one_jvalue = { 1 }; // Emit bytecodes for the given invoke instruction. MethodHandleWalker::ArgToken @@ -964,18 +1044,18 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, MethodHandleWalker::ArgToken* argv, TRAPS) { + ArgToken zero; if (m == NULL) { // Get the intrinsic methodOop. m = vmIntrinsics::method_for(iid); if (m == NULL) { - ArgToken zero; lose(vmIntrinsics::name_at(iid), CHECK_(zero)); } } - klassOop klass = m->method_holder(); - Symbol* name = m->name(); - Symbol* signature = m->signature(); + klassOop klass = m->method_holder(); + Symbol* name = m->name(); + Symbol* signature = m->signature(); if (tailcall) { // Actually, in order to make these methods more recognizable, @@ -1041,7 +1121,6 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, if (rbt != _rtype) { if (rbt == T_VOID) { // push a zero of the right sort - ArgToken zero; if (_rtype == T_OBJECT) { zero = make_oop_constant(NULL, CHECK_(zero)); } else { @@ -1051,9 +1130,27 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, } else if (_rtype == T_VOID) { // We'll emit a _return with something on the stack. // It's OK to ignore what's on the stack. + } else if (rbt == T_INT && is_subword_type(_rtype)) { + // Convert value to match return type. + switch (_rtype) { + case T_BOOLEAN: { + // boolean is treated as a one-bit unsigned integer. + // Cf. API documentation: java/lang/invoke/MethodHandles.html#explicitCastArguments + ArgToken one = make_prim_constant(T_INT, &one_jvalue, CHECK_(zero)); + emit_load_constant(one); + emit_bc(Bytecodes::_iand); + break; + } + case T_BYTE: emit_bc(Bytecodes::_i2b); break; + case T_CHAR: emit_bc(Bytecodes::_i2c); break; + case T_SHORT: emit_bc(Bytecodes::_i2s); break; + default: ShouldNotReachHere(); + } + } else if (is_subword_type(rbt) && (is_subword_type(_rtype) || (_rtype == T_INT))) { + // The subword type was returned as an int and will be passed + // on as an int. } else { - tty->print_cr("*** rbt=%d != rtype=%d", rbt, _rtype); - assert(false, "IMPLEMENT ME"); + lose("unknown conversion", CHECK_(zero)); } } switch (_rtype) { @@ -1216,17 +1313,17 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const { typeArrayHandle exception_handlers(THREAD, Universe::the_empty_int_array()); m->set_exception_table(exception_handlers()); - // Set the invocation counter's count to the invoke count of the - // original call site. - InvocationCounter* ic = m->invocation_counter(); - ic->set(InvocationCounter::wait_for_compile, _invoke_count); - // Rewrite the method and set up the constant pool cache. objArrayOop m_array = oopFactory::new_system_objArray(1, CHECK_(empty)); objArrayHandle methods(THREAD, m_array); methods->obj_at_put(0, m()); Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(empty)); // Use fake class. + // Set the invocation counter's count to the invoke count of the + // original call site. + InvocationCounter* ic = m->invocation_counter(); + ic->set(InvocationCounter::wait_for_compile, _invoke_count); + // Create a new MDO { methodDataOop mdo = oopFactory::new_methodData(m, CHECK_(empty)); @@ -1235,10 +1332,12 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const { // Iterate over all profile data and set the count of the counter // data entries to the original call site counter. - for (ProfileData* pd = mdo->first_data(); mdo->is_valid(pd); pd = mdo->next_data(pd)) { - if (pd->is_CounterData()) { - CounterData* cd = pd->as_CounterData(); - cd->set_count(_invoke_count); + for (ProfileData* profile_data = mdo->first_data(); + mdo->is_valid(profile_data); + profile_data = mdo->next_data(profile_data)) { + if (profile_data->is_CounterData()) { + CounterData* counter_data = profile_data->as_CounterData(); + counter_data->set_count(_invoke_count); } } } @@ -1257,7 +1356,6 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const { #ifndef PRODUCT -#if 0 // MH printer for debugging. class MethodHandlePrinter : public MethodHandleWalker { @@ -1265,6 +1363,7 @@ private: outputStream* _out; bool _verbose; int _temp_num; + int _param_state; stringStream _strbuf; const char* strbuf() { const char* s = _strbuf.as_string(); @@ -1272,14 +1371,21 @@ private: return s; } ArgToken token(const char* str) { - return (ArgToken) str; + jvalue string_con; + string_con.j = (intptr_t) str; + return ArgToken(tt_symbolic, T_LONG, string_con); + } + const char* string(ArgToken token) { + return (const char*) (intptr_t) token.get_jlong(); } void start_params() { + _param_state <<= 1; _out->print("("); } void end_params() { if (_verbose) _out->print("\n"); _out->print(") => {"); + _param_state >>= 1; } void put_type_name(BasicType type, klassOop tk, outputStream* s) { const char* kname = NULL; @@ -1299,9 +1405,10 @@ private: public: MethodHandlePrinter(Handle root, bool verbose, outputStream* out, TRAPS) - : MethodHandleWalker(root, THREAD), + : MethodHandleWalker(root, false, THREAD), _out(out), _verbose(verbose), + _param_state(0), _temp_num(0) { start_params(); @@ -1309,9 +1416,10 @@ public: virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) { if (argnum < 0) { end_params(); - return NULL; + return token("return"); } - if (argnum == 0) { + if ((_param_state & 1) == 0) { + _param_state |= 1; _out->print(_verbose ? "\n " : ""); } else { _out->print(_verbose ? ",\n " : ", "); @@ -1341,8 +1449,15 @@ public: java_lang_boxing_object::print(type, con, &_strbuf); return maybe_make_temp("constant", type, "k"); } - virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken src, TRAPS) { - _strbuf.print("%s(%s", Bytecodes::name(op), (const char*)src); + void print_bytecode_name(Bytecodes::Code op) { + if (Bytecodes::is_defined(op)) + _strbuf.print("%s", Bytecodes::name(op)); + else + _strbuf.print("bytecode_%d", (int) op); + } + virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS) { + print_bytecode_name(op); + _strbuf.print("(%s", string(src)); if (tk != NULL) { _strbuf.print(", "); put_type_name(type, tk, &_strbuf); @@ -1350,8 +1465,8 @@ public: _strbuf.print(")"); return maybe_make_temp("convert", type, "v"); } - virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken base, ArgToken offset, TRAPS) { - _strbuf.print("%s(%s, %s", Bytecodes::name(op), (const char*)base, (const char*)offset); + virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS) { + _strbuf.print("%s(%s, %s", Bytecodes::name(op), string(base), string(offset)); if (tk != NULL) { _strbuf.print(", "); put_type_name(type, tk, &_strbuf); @@ -1362,7 +1477,8 @@ public: virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS) { - Symbol* name, sig; + Symbol* name; + Symbol* sig; if (m != NULL) { name = m->name(); sig = m->signature(); @@ -1372,7 +1488,7 @@ public: } _strbuf.print("%s %s%s(", Bytecodes::name(op), name->as_C_string(), sig->as_C_string()); for (int i = 0; i < argc; i++) { - _strbuf.print("%s%s", (i > 0 ? ", " : ""), (const char*)argv[i]); + _strbuf.print("%s%s", (i > 0 ? ", " : ""), string(argv[i])); } _strbuf.print(")"); if (!tailcall) { @@ -1410,24 +1526,20 @@ public: if (HAS_PENDING_EXCEPTION) { oop ex = PENDING_EXCEPTION; CLEAR_PENDING_EXCEPTION; - out->print("\n*** "); - if (ex != Universe::virtual_machine_error_instance()) - ex->print_on(out); - else - out->print("lose: %s", printer.lose_message()); - out->print("\n}\n"); + out->print(" *** "); + if (printer.lose_message() != NULL) out->print("%s ", printer.lose_message()); + out->print("}"); } out->print("\n"); } }; -#endif // 0 extern "C" void print_method_handle(oop mh) { if (!mh->is_oop()) { - tty->print_cr("*** not a method handle: "INTPTR_FORMAT, (intptr_t)mh); + tty->print_cr("*** not a method handle: "PTR_FORMAT, (intptr_t)mh); } else if (java_lang_invoke_MethodHandle::is_instance(mh)) { - //MethodHandlePrinter::print(mh); + MethodHandlePrinter::print(mh); } else { tty->print("*** not a method handle: "); mh->print(); diff --git a/hotspot/src/share/vm/prims/methodHandleWalk.hpp b/hotspot/src/share/vm/prims/methodHandleWalk.hpp index 6fb2e986ee6..df2fe278f8c 100644 --- a/hotspot/src/share/vm/prims/methodHandleWalk.hpp +++ b/hotspot/src/share/vm/prims/methodHandleWalk.hpp @@ -113,6 +113,7 @@ public: tt_parameter, tt_temporary, tt_constant, + tt_symbolic, tt_illegal }; @@ -164,6 +165,10 @@ private: bool _for_invokedynamic; int _local_index; + // This array is kept in an unusual order, indexed by low-level "slot number". + // TOS is always _outgoing.at(0), so simple pushes and pops shift the whole _outgoing array. + // If there is a receiver in the current argument list, it is at _outgoing.at(_outgoing.length()-1). + // If a value at _outgoing.at(n) is T_LONG or T_DOUBLE, the value at _outgoing.at(n+1) is T_VOID. GrowableArray _outgoing; // current outgoing parameter slots int _outgoing_argc; // # non-empty outgoing slots @@ -173,6 +178,11 @@ private: // Insert or delete a second empty slot as needed. void change_argument(BasicType old_type, int slot, BasicType new_type, const ArgToken& new_arg); + // Raw retype conversions for OP_RAW_RETYPE. + void retype_raw_conversion(BasicType src, BasicType dst, bool for_return, int slot, TRAPS); + void retype_raw_argument_type(BasicType src, BasicType dst, int slot, TRAPS) { retype_raw_conversion(src, dst, false, slot, CHECK); } + void retype_raw_return_type( BasicType src, BasicType dst, TRAPS) { retype_raw_conversion(src, dst, true, -1, CHECK); } + SlotState* slot_state(int slot) { if (slot < 0 || slot >= _outgoing.length()) return NULL; @@ -221,12 +231,12 @@ public: int max_locals() const { return _local_index; } // plug-in abstract interpretation steps: - virtual ArgToken make_parameter( BasicType type, klassOop tk, int argnum, TRAPS ) = 0; - virtual ArgToken make_prim_constant( BasicType type, jvalue* con, TRAPS ) = 0; - virtual ArgToken make_oop_constant( oop con, TRAPS ) = 0; - virtual ArgToken make_conversion( BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS ) = 0; - virtual ArgToken make_fetch( BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS ) = 0; - virtual ArgToken make_invoke( methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS ) = 0; + virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) = 0; + virtual ArgToken make_prim_constant(BasicType type, jvalue* con, TRAPS) = 0; + virtual ArgToken make_oop_constant(oop con, TRAPS) = 0; + virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS) = 0; + virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS) = 0; + virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS) = 0; // For make_invoke, the methodOop can be NULL if the intrinsic ID // is something other than vmIntrinsics::_none. @@ -253,6 +263,10 @@ private: KlassHandle _target_klass; Thread* _thread; + // Values used by the compiler. + static jvalue zero_jvalue; + static jvalue one_jvalue; + // Fake constant pool entry. class ConstantValue { private: @@ -417,7 +431,7 @@ private: methodHandle get_method_oop(TRAPS) const; public: - MethodHandleCompiler(Handle root, methodHandle call_method, int invoke_count, bool for_invokedynamic, TRAPS); + MethodHandleCompiler(Handle root, methodHandle callee, int invoke_count, bool for_invokedynamic, TRAPS); // Compile the given MH chain into bytecode. methodHandle compile(TRAPS); diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp index f50ce0e57ea..293a2771bb4 100644 --- a/hotspot/src/share/vm/prims/methodHandles.cpp +++ b/hotspot/src/share/vm/prims/methodHandles.cpp @@ -1099,6 +1099,14 @@ static oop object_java_mirror() { return Klass::cast(SystemDictionary::Object_klass())->java_mirror(); } +bool MethodHandles::is_float_fixed_reinterpretation_cast(BasicType src, BasicType dst) { + if (src == T_FLOAT) return dst == T_INT; + if (src == T_INT) return dst == T_FLOAT; + if (src == T_DOUBLE) return dst == T_LONG; + if (src == T_LONG) return dst == T_DOUBLE; + return false; +} + bool MethodHandles::same_basic_type_for_arguments(BasicType src, BasicType dst, bool raw, @@ -1125,10 +1133,8 @@ bool MethodHandles::same_basic_type_for_arguments(BasicType src, return true; // remaining case: byte fits in short } // allow float/fixed reinterpretation casts - if (src == T_FLOAT) return dst == T_INT; - if (src == T_INT) return dst == T_FLOAT; - if (src == T_DOUBLE) return dst == T_LONG; - if (src == T_LONG) return dst == T_DOUBLE; + if (is_float_fixed_reinterpretation_cast(src, dst)) + return true; return false; } @@ -1399,7 +1405,7 @@ const char* MethodHandles::check_argument_type_change(BasicType src_type, int argnum, bool raw) { const char* err = NULL; - bool for_return = (argnum < 0); + const bool for_return = (argnum < 0); // just in case: if (src_type == T_ARRAY) src_type = T_OBJECT; @@ -1408,17 +1414,17 @@ const char* MethodHandles::check_argument_type_change(BasicType src_type, // Produce some nice messages if VerifyMethodHandles is turned on: if (!same_basic_type_for_arguments(src_type, dst_type, raw, for_return)) { if (src_type == T_OBJECT) { - if (raw && dst_type == T_INT && is_always_null_type(src_klass)) - return NULL; // OK to convert a null pointer to a garbage int - err = ((argnum >= 0) + if (raw && is_java_primitive(dst_type)) + return NULL; // ref-to-prim discards ref and returns zero + err = (!for_return ? "type mismatch: passing a %s for method argument #%d, which expects primitive %s" : "type mismatch: returning a %s, but caller expects primitive %s"); } else if (dst_type == T_OBJECT) { - err = ((argnum >= 0) + err = (!for_return ? "type mismatch: passing a primitive %s for method argument #%d, which expects %s" : "type mismatch: returning a primitive %s, but caller expects %s"); } else { - err = ((argnum >= 0) + err = (!for_return ? "type mismatch: passing a %s for method argument #%d, which expects %s" : "type mismatch: returning a %s, but caller expects %s"); } @@ -1427,11 +1433,11 @@ const char* MethodHandles::check_argument_type_change(BasicType src_type, if (!class_cast_needed(dst_klass, src_klass)) { if (raw) return NULL; // reverse cast is OK; the MH target is trusted to enforce it - err = ((argnum >= 0) + err = (!for_return ? "cast required: passing a %s for method argument #%d, which expects %s" : "cast required: returning a %s, but caller expects %s"); } else { - err = ((argnum >= 0) + err = (!for_return ? "reference mismatch: passing a %s for method argument #%d, which expects %s" : "reference mismatch: returning a %s, but caller expects %s"); } @@ -1452,7 +1458,7 @@ const char* MethodHandles::check_argument_type_change(BasicType src_type, size_t msglen = strlen(err) + strlen(src_name) + strlen(dst_name) + (argnum < 10 ? 1 : 11); char* msg = NEW_RESOURCE_ARRAY(char, msglen + 1); - if (argnum >= 0) { + if (!for_return) { assert(strstr(err, "%d") != NULL, ""); jio_snprintf(msg, msglen, err, src_name, argnum, dst_name); } else { @@ -2180,9 +2186,10 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { } void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS) { - int argslot = java_lang_invoke_AdapterMethodHandle::vmargslot(mh()); - jint conversion = java_lang_invoke_AdapterMethodHandle::conversion(mh()); - jint conv_op = adapter_conversion_op(conversion); + Handle argument = java_lang_invoke_AdapterMethodHandle::argument(mh()); + int argslot = java_lang_invoke_AdapterMethodHandle::vmargslot(mh()); + jint conversion = java_lang_invoke_AdapterMethodHandle::conversion(mh()); + jint conv_op = adapter_conversion_op(conversion); // adjust the adapter code to the internal EntryKind enumeration: EntryKind ek_orig = adapter_entry_kind(conv_op); @@ -2241,7 +2248,7 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu } else if (src == T_DOUBLE && dest == T_FLOAT) { ek_opt = _adapter_opt_d2f; } else { - assert(false, ""); + goto throw_not_impl; // runs user code, hence could block } break; case 1 *4+ 2: @@ -2250,11 +2257,11 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu } else if (src == T_FLOAT && dest == T_DOUBLE) { ek_opt = _adapter_opt_f2d; } else { - assert(false, ""); + goto throw_not_impl; // runs user code, hence could block } break; default: - assert(false, ""); + goto throw_not_impl; // runs user code, hence could block break; } } @@ -2271,7 +2278,7 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu ek_opt = _adapter_opt_unboxl; break; default: - assert(false, ""); + goto throw_not_impl; break; } } @@ -2284,6 +2291,9 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu vminfo = argslot; ek_opt = _adapter_opt_collect_ref; ensure_vmlayout_field(target, CHECK); + // for MethodHandleWalk: + if (java_lang_invoke_AdapterMethodHandle::is_instance(argument())) + ensure_vmlayout_field(argument, CHECK); if (!OptimizeMethodHandles) break; switch (type2size[src]) { case 1: @@ -2311,7 +2321,7 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu ek_opt = _adapter_opt_collect_2_ref; break; default: - assert(false, ""); + goto throw_not_impl; break; } } @@ -2335,7 +2345,7 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu rotate > 0 ? _adapter_opt_rot_2_up : _adapter_opt_rot_2_down); break; default: - assert(false, ""); + goto throw_not_impl; break; } } @@ -2402,12 +2412,11 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu case _adapter_collect_args: { assert(UseRicochetFrames, "else don't come here"); - int elem_slots = argument_slot_count( - java_lang_invoke_MethodHandle::type( - java_lang_invoke_AdapterMethodHandle::argument(mh()) ) ); + int elem_slots = argument_slot_count(java_lang_invoke_MethodHandle::type(argument())); // vminfo will be the location to insert the return value vminfo = argslot; ensure_vmlayout_field(target, CHECK); + ensure_vmlayout_field(argument, CHECK); // general case: switch (dest) { @@ -2472,12 +2481,11 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu case _adapter_fold_args: { assert(UseRicochetFrames, "else don't come here"); - int elem_slots = argument_slot_count( - java_lang_invoke_MethodHandle::type( - java_lang_invoke_AdapterMethodHandle::argument(mh()) ) ); + int elem_slots = argument_slot_count(java_lang_invoke_MethodHandle::type(argument())); // vminfo will be the location to insert the return value vminfo = argslot + elem_slots; ensure_vmlayout_field(target, CHECK); + ensure_vmlayout_field(argument, CHECK); switch (dest) { default : if (!is_subword_type(dest)) goto throw_not_impl; @@ -2527,15 +2535,31 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu break; } - if (err != NULL && (vminfo & CONV_VMINFO_MASK) != vminfo) { + if (err == NULL && (vminfo & CONV_VMINFO_MASK) != vminfo) { // should not happen, since vminfo is used to encode arg/slot indexes < 255 err = "vminfo overflow"; } - if (err != NULL && !have_entry(ek_opt)) { + if (err == NULL && !have_entry(ek_opt)) { err = "adapter stub for this kind of method handle is missing"; } + if (err == NULL && ek_opt == ek_orig) { + switch (ek_opt) { + case _adapter_prim_to_prim: + case _adapter_ref_to_prim: + case _adapter_prim_to_ref: + case _adapter_swap_args: + case _adapter_rot_args: + case _adapter_collect_args: + case _adapter_fold_args: + case _adapter_spread_args: + // should be handled completely by optimized cases; see above + err = "init_AdapterMethodHandle should not issue this"; + break; + } + } + if (err != NULL) { throw_InternalError_for_bad_conversion(conversion, err, THREAD); return; diff --git a/hotspot/src/share/vm/prims/methodHandles.hpp b/hotspot/src/share/vm/prims/methodHandles.hpp index 537054989bd..66de7efe0f4 100644 --- a/hotspot/src/share/vm/prims/methodHandles.hpp +++ b/hotspot/src/share/vm/prims/methodHandles.hpp @@ -698,6 +698,8 @@ class MethodHandles: AllStatic { KlassHandle receiver_klass, TRAPS); +public: + static bool is_float_fixed_reinterpretation_cast(BasicType src, BasicType dst); static bool same_basic_type_for_arguments(BasicType src, BasicType dst, bool raw = false, bool for_return = false); From 2f060b40b5b3146885f2907650f3878f28ba155e Mon Sep 17 00:00:00 2001 From: Suchen Chien Date: Thu, 12 May 2011 17:17:14 -0700 Subject: [PATCH 44/65] Added tag jdk7-b142 for changeset 70e694b33e64 --- .hgtags-top-repo | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags-top-repo b/.hgtags-top-repo index 6745af50b29..8c8e07b841c 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -116,3 +116,4 @@ fc47c97bbbd91b1f774d855c48a7e285eb1a351a jdk7-b138 7ed6d0b9aaa12320832a7ddadb88d6d8d0dda4c1 jdk7-b139 dcfe74f1c6553c556e7d361c30b0b614eb5e40f6 jdk7-b140 c6569c5585851dfd39b8de8e021c3c312f51af12 jdk7-b141 +cfbbdb77eac0397b03eb99ee2e07ea00e0a7b81e jdk7-b142 From 288dc3d0c3d0387f2c2e9bce64c061dfa203bd6f Mon Sep 17 00:00:00 2001 From: Suchen Chien Date: Thu, 12 May 2011 17:17:16 -0700 Subject: [PATCH 45/65] Added tag jdk7-b142 for changeset f1451ca1fca3 --- corba/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/corba/.hgtags b/corba/.hgtags index 9624f1f3878..2f400d34aab 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -116,3 +116,4 @@ a66c01d8bf895261715955df0b95545c000ed6a8 jdk7-b137 60b074ec6fcf5cdf9efce22fdfb02326ed8fa2d3 jdk7-b139 cdf5d19ec142424489549025e9c42e51f32cf688 jdk7-b140 a58635cdd921bafef353f4864184a0481353197b jdk7-b141 +a2f340a048c88d10cbedc0504f5cf03d39925a40 jdk7-b142 From 81fa17dbea6250aa142e587e16b641e7b48a6511 Mon Sep 17 00:00:00 2001 From: Suchen Chien Date: Thu, 12 May 2011 17:17:22 -0700 Subject: [PATCH 46/65] Added tag jdk7-b142 for changeset 19acba38f929 --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 5e01fb71812..f7a4fe8e7f8 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -170,3 +170,4 @@ d283b82966712b353fa307845a1316da42a355f4 jdk7-b140 d283b82966712b353fa307845a1316da42a355f4 hs21-b10 5d07913abd59261c77f24cc04a759cb75d804099 jdk7-b141 3aea9e9feb073f5500e031be6186666bcae89aa2 hs21-b11 +9ad1548c6b63d596c411afc35147ffd5254426d9 jdk7-b142 From a852fe1092d3b8b9b3d9c3ff5bb27e7380ec693b Mon Sep 17 00:00:00 2001 From: Suchen Chien Date: Thu, 12 May 2011 17:17:29 -0700 Subject: [PATCH 47/65] Added tag jdk7-b142 for changeset 80cd3986b5de --- jaxp/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxp/.hgtags b/jaxp/.hgtags index a61349e7ee7..ddd628c1171 100644 --- a/jaxp/.hgtags +++ b/jaxp/.hgtags @@ -116,3 +116,4 @@ be3758943770a0a3dd4be6a1cb4063507c4d7062 jdk7-b138 28c7c0ed2444607829ba11ad827f8d52197a2830 jdk7-b139 c8136fd161c83917f87e93b14fa2ba3483f9be83 jdk7-b140 e1b5ef243445bf836d095fd44866e1771ef99374 jdk7-b141 +7d067af4b25e4b7e6b28bef48527d67f8650e6c5 jdk7-b142 From 4a5e1727dc15695c2d1af320a3b2574d456483ff Mon Sep 17 00:00:00 2001 From: Suchen Chien Date: Thu, 12 May 2011 17:17:30 -0700 Subject: [PATCH 48/65] Added tag jdk7-b142 for changeset 837252ffce27 --- jaxws/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxws/.hgtags b/jaxws/.hgtags index 9fc1479b23f..96c0d88cb82 100644 --- a/jaxws/.hgtags +++ b/jaxws/.hgtags @@ -116,3 +116,4 @@ cc956c8a8255583535597e9a63db23c510e9a063 jdk7-b138 c025078c8362076503bb83b8e4da14ba7b347940 jdk7-b139 82a9022c4f21b1313023c8303b557a17c4106701 jdk7-b140 66826b0aec5a1834da3821c35cf85ac154e9b04d jdk7-b141 +0ef3ef823c39eee3d670e58027c3219cb66f0283 jdk7-b142 From 19d2501437298ad399cc06f336ba78db793fa3e0 Mon Sep 17 00:00:00 2001 From: Suchen Chien Date: Thu, 12 May 2011 17:17:36 -0700 Subject: [PATCH 49/65] Added tag jdk7-b142 for changeset f09a2b7a4eeb --- jdk/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/.hgtags b/jdk/.hgtags index 8a7ff58323d..ebd78b31459 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -116,3 +116,4 @@ aa13e7702cd9d8aca9aa38f1227f966990866944 jdk7-b136 d80954a89b49fda47c0c5cace65a17f5a758b8bd jdk7-b139 9315c733fb17ddfb9fb44be7e0ffea37bf3c727d jdk7-b140 63eeefe118da18c75ba3d36266768cd1ccaaca6b jdk7-b141 +312612e89ece62633f4809706dec00bcd5fe7c2d jdk7-b142 From eb9632506bb15ab387673aa480437d0d97c73416 Mon Sep 17 00:00:00 2001 From: John R Rose Date: Thu, 12 May 2011 19:27:33 -0700 Subject: [PATCH 50/65] 7034977: JSR 292 MethodHandle.invokeGeneric should be renamed MethodHandle.invoke Rename invokeGeneric to invoke Reviewed-by: never, twisti --- .../classes/java/lang/invoke/CallSite.java | 8 +- .../classes/java/lang/invoke/FromGeneric.java | 1 - .../java/lang/invoke/InvokeGeneric.java | 6 +- .../classes/java/lang/invoke/Invokers.java | 20 +-- .../java/lang/invoke/MethodHandle.java | 117 ++++++++++-------- .../java/lang/invoke/MethodHandleNatives.java | 2 +- .../java/lang/invoke/MethodHandles.java | 41 +++--- .../classes/java/lang/invoke/MethodType.java | 4 +- .../java/lang/invoke/MethodTypeForm.java | 6 +- .../java/lang/invoke/package-info.java | 16 +-- .../java/lang/invoke/InvokeGenericTest.java | 32 +++-- .../java/lang/invoke/JavaDocExamplesTest.java | 18 +-- .../java/lang/invoke/MethodHandlesTest.java | 2 +- 13 files changed, 155 insertions(+), 118 deletions(-) diff --git a/jdk/src/share/classes/java/lang/invoke/CallSite.java b/jdk/src/share/classes/java/lang/invoke/CallSite.java index b2da146d261..0924a438c80 100644 --- a/jdk/src/share/classes/java/lang/invoke/CallSite.java +++ b/jdk/src/share/classes/java/lang/invoke/CallSite.java @@ -273,9 +273,9 @@ public class CallSite { Object binding; info = maybeReBox(info); if (info == null) { - binding = bootstrapMethod.invokeGeneric(caller, name, type); + binding = bootstrapMethod.invoke(caller, name, type); } else if (!info.getClass().isArray()) { - binding = bootstrapMethod.invokeGeneric(caller, name, type, info); + binding = bootstrapMethod.invoke(caller, name, type, info); } else { Object[] argv = (Object[]) info; maybeReBoxElements(argv); @@ -283,10 +283,10 @@ public class CallSite { throw new BootstrapMethodError("too many bootstrap method arguments"); MethodType bsmType = bootstrapMethod.type(); if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class) - binding = bootstrapMethod.invokeGeneric(caller, name, type, argv); + binding = bootstrapMethod.invoke(caller, name, type, argv); else binding = MethodHandles.spreadInvoker(bsmType, 3) - .invokeGeneric(bootstrapMethod, caller, name, type, argv); + .invoke(bootstrapMethod, caller, name, type, argv); } //System.out.println("BSM for "+name+type+" => "+binding); if (binding instanceof CallSite) { diff --git a/jdk/src/share/classes/java/lang/invoke/FromGeneric.java b/jdk/src/share/classes/java/lang/invoke/FromGeneric.java index 1c0523a4a36..9a48c09a35d 100644 --- a/jdk/src/share/classes/java/lang/invoke/FromGeneric.java +++ b/jdk/src/share/classes/java/lang/invoke/FromGeneric.java @@ -160,7 +160,6 @@ class FromGeneric { /** Build an adapter of the given generic type, which invokes typedTarget * on the incoming arguments, after unboxing as necessary. * The return value is boxed if necessary. - * @param genericType the required type of the result * @param typedTarget the target * @return an adapter method handle */ diff --git a/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java b/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java index a235e7318da..a747090b9a1 100644 --- a/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java +++ b/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java @@ -29,12 +29,12 @@ import sun.invoke.util.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; /** - * Adapters which manage MethodHandle.invokeGeneric calls. + * Adapters which manage inexact MethodHandle.invoke calls. * The JVM calls one of these when the exact type match fails. * @author jrose */ class InvokeGeneric { - // erased type for the call, which originates from an invokeGeneric site + // erased type for the call, which originates from an inexact invoke site private final MethodType erasedCallerType; // an invoker of type (MT, MH; A...) -> R private final MethodHandle initialInvoker; @@ -56,7 +56,7 @@ class InvokeGeneric { } /** Return the adapter information for this type's erasure. */ - /*non-public*/ static MethodHandle genericInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException { + /*non-public*/ static MethodHandle generalInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException { InvokeGeneric gen = new InvokeGeneric(erasedCallerType); return gen.initialInvoker; } diff --git a/jdk/src/share/classes/java/lang/invoke/Invokers.java b/jdk/src/share/classes/java/lang/invoke/Invokers.java index 4eeb36f76b7..c142224bd4a 100644 --- a/jdk/src/share/classes/java/lang/invoke/Invokers.java +++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java @@ -43,10 +43,10 @@ class Invokers { private /*lazy*/ MethodHandle erasedInvoker; /*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric - // generic (untyped) invoker for the outgoing call - private /*lazy*/ MethodHandle genericInvoker; + // general invoker for the outgoing call + private /*lazy*/ MethodHandle generalInvoker; - // generic (untyped) invoker for the outgoing call; accepts a single Object[] + // general invoker for the outgoing call; accepts a single Object[] private final /*lazy*/ MethodHandle[] spreadInvokers; // invoker for an unbound callsite @@ -77,13 +77,13 @@ class Invokers { return invoker; } - /*non-public*/ MethodHandle genericInvoker() { + /*non-public*/ MethodHandle generalInvoker() { MethodHandle invoker1 = exactInvoker(); - MethodHandle invoker = genericInvoker; + MethodHandle invoker = generalInvoker; if (invoker != null) return invoker; - MethodType genericType = targetType.generic(); - invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType)); - genericInvoker = invoker; + MethodType generalType = targetType.generic(); + invoker = MethodHandles.convertArguments(invoker1, invokerType(generalType)); + generalInvoker = invoker; return invoker; } @@ -93,7 +93,7 @@ class Invokers { if (invoker != null) return invoker; MethodType erasedType = targetType.erase(); if (erasedType == targetType.generic()) - invoker = genericInvoker(); + invoker = generalInvoker(); else invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType)); erasedInvoker = invoker; @@ -103,7 +103,7 @@ class Invokers { /*non-public*/ MethodHandle spreadInvoker(int objectArgCount) { MethodHandle vaInvoker = spreadInvokers[objectArgCount]; if (vaInvoker != null) return vaInvoker; - MethodHandle gInvoker = genericInvoker(); + MethodHandle gInvoker = generalInvoker(); vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount); spreadInvokers[objectArgCount] = vaInvoker; return vaInvoker; diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java index 8165915c7bb..31cd782e9ec 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java @@ -53,12 +53,12 @@ import static java.lang.invoke.MethodHandleStatics.*; * and the kinds of transformations that apply to it. *

    * A method handle contains a pair of special invoker methods - * called {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}. + * called {@link #invokeExact invokeExact} and {@link #invoke invoke}. * Both invoker methods provide direct access to the method handle's * underlying method, constructor, field, or other operation, * as modified by transformations of arguments and return values. * Both invokers accept calls which exactly match the method handle's own type. - * The {@code invokeGeneric} invoker also accepts a range of other call types. + * The plain, inexact invoker also accepts a range of other call types. *

    * Method handles are immutable and have no visible state. * Of course, they can be bound to underlying methods or data which exhibit state. @@ -76,7 +76,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * may change from time to time or across implementations from different vendors. * *

    Method handle compilation

    - * A Java method call expression naming {@code invokeExact} or {@code invokeGeneric} + * A Java method call expression naming {@code invokeExact} or {@code invoke} * can invoke a method handle from Java source code. * From the viewpoint of source code, these methods can take any arguments * and their result can be cast to any return type. @@ -86,7 +86,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * which connects this freedom of invocation directly to the JVM execution stack. *

    * As is usual with virtual methods, source-level calls to {@code invokeExact} - * and {@code invokeGeneric} compile to an {@code invokevirtual} instruction. + * and {@code invoke} compile to an {@code invokevirtual} instruction. * More unusually, the compiler must record the actual argument types, * and may not perform method invocation conversions on the arguments. * Instead, it must push them on the stack according to their own unconverted types. @@ -109,7 +109,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * The first time a {@code invokevirtual} instruction is executed * it is linked, by symbolically resolving the names in the instruction * and verifying that the method call is statically legal. - * This is true of calls to {@code invokeExact} and {@code invokeGeneric}. + * This is true of calls to {@code invokeExact} and {@code invoke}. * In this case, the type descriptor emitted by the compiler is checked for * correct syntax and names it contains are resolved. * Thus, an {@code invokevirtual} instruction which invokes @@ -127,18 +127,18 @@ import static java.lang.invoke.MethodHandleStatics.*; * In the case of {@code invokeExact}, the type descriptor of the invocation * (after resolving symbolic type names) must exactly match the method type * of the receiving method handle. - * In the case of {@code invokeGeneric}, the resolved type descriptor + * In the case of plain, inexact {@code invoke}, the resolved type descriptor * must be a valid argument to the receiver's {@link #asType asType} method. - * Thus, {@code invokeGeneric} is more permissive than {@code invokeExact}. + * Thus, plain {@code invoke} is more permissive than {@code invokeExact}. *

    * After type matching, a call to {@code invokeExact} directly * and immediately invoke the method handle's underlying method * (or other behavior, as the case may be). *

    - * A call to {@code invokeGeneric} works the same as a call to + * A call to plain {@code invoke} works the same as a call to * {@code invokeExact}, if the type descriptor specified by the caller * exactly matches the method handle's own type. - * If there is a type mismatch, {@code invokeGeneric} attempts + * If there is a type mismatch, {@code invoke} attempts * to adjust the type of the receiving method handle, * as if by a call to {@link #asType asType}, * to obtain an exactly invokable method handle {@code M2}. @@ -152,7 +152,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * In typical programs, method handle type matching will usually succeed. * But if a match fails, the JVM will throw a {@link WrongMethodTypeException}, * either directly (in the case of {@code invokeExact}) or indirectly as if - * by a failed call to {@code asType} (in the case of {@code invokeGeneric}). + * by a failed call to {@code asType} (in the case of {@code invoke}). *

    * Thus, a method type mismatch which might show up as a linkage error * in a statically typed program can show up as @@ -249,8 +249,8 @@ assert(s.equals("savvy")); mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); -x = mh.invokeGeneric("one", "two"); -// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; +x = mh.invoke("one", "two"); +// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; assert(x.equals(java.util.Arrays.asList("one","two"))); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); @@ -269,12 +269,12 @@ mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); mh.invokeExact(System.out, "Hello, world."); // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V * - * Each of the above calls to {@code invokeExact} or {@code invokeGeneric} + * Each of the above calls to {@code invokeExact} or plain {@code invoke} * generates a single invokevirtual instruction with * the type descriptor indicated in the following comment. * *

    Exceptions

    - * The methods {@code invokeExact} and {@code invokeGeneric} are declared + * The methods {@code invokeExact} and {@code invoke} are declared * to throw {@link java.lang.Throwable Throwable}, * which is to say that there is no static restriction on what a method handle * can throw. Since the JVM does not distinguish between checked @@ -288,7 +288,7 @@ mh.invokeExact(System.out, "Hello, world."); * *

    Signature polymorphism

    * The unusual compilation and linkage behavior of - * {@code invokeExact} and {@code invokeGeneric} + * {@code invokeExact} and plain {@code invoke} * is referenced by the term signature polymorphism. * A signature polymorphic method is one which can operate with * any of a wide range of call signatures and return types. @@ -322,7 +322,7 @@ mh.invokeExact(System.out, "Hello, world."); * The following methods (and no others) are signature polymorphic: *
      *
    • {@link java.lang.invoke.MethodHandle#invokeExact MethodHandle.invokeExact} - *
    • {@link java.lang.invoke.MethodHandle#invokeGeneric MethodHandle.invokeGeneric} + *
    • {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke} *
    *

    * A signature polymorphic method will be declared with the following properties: @@ -374,24 +374,34 @@ mh.invokeExact(System.out, "Hello, world."); *

    * As a special case, * when the Core Reflection API is used to view the signature polymorphic - * methods {@code invokeExact} or {@code invokeGeneric} in this class, - * they appear as single, non-polymorphic native methods. - * Calls to these native methods do not result in method handle invocations. + * methods {@code invokeExact} or plain {@code invoke} in this class, + * they appear as ordinary non-polymorphic methods. + * Their reflective appearance, as viewed by + * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod}, + * is unaffected by their special status in this API. + * For example, {@link java.lang.reflect.Method#getModifiers Method.getModifiers} + * will report exactly those modifier bits required for any similarly + * declared method, including in this case {@code native} and {@code varargs} bits. + *

    + * As with any reflected method, these methods (when reflected) may be + * invoked via {@link java.lang.reflect.Method#invoke Method.invoke}. + * However, such reflective calls do not result in method handle invocations. + * Such a call, if passed the required argument + * (a single one, of type {@code Object[]}), will ignore the argument and + * will throw an {@code UnsupportedOperationException}. + *

    * Since {@code invokevirtual} instructions can natively * invoke method handles under any type descriptor, this reflective view conflicts - * with the normal presentation via bytecodes. - * Thus, these two native methods, as viewed by - * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod}, - * are placeholders only. - * If invoked via {@link java.lang.reflect.Method#invoke Method.invoke}, - * they will throw {@code UnsupportedOperationException}. + * with the normal presentation of these methods via bytecodes. + * Thus, these two native methods, when reflectively viewed by + * {@code Class.getDeclaredMethod}, may be regarded as placeholders only. *

    * In order to obtain an invoker method for a particular type descriptor, * use {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker}, - * or {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker}. + * or {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}. * The {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual} * API is also able to return a method handle - * to call {@code invokeExact} or {@code invokeGeneric}, + * to call {@code invokeExact} or plain {@code invoke}, * for any specified type descriptor . * *

    Interoperation between method handles and Java generics

    @@ -523,7 +533,7 @@ public abstract class MethodHandle { * adaptations directly on the caller's arguments, * and call the target method handle according to its own exact type. *

    - * The type descriptor at the call site of {@code invokeGeneric} must + * The type descriptor at the call site of {@code invoke} must * be a valid argument to the receivers {@code asType} method. * In particular, the caller must specify the same argument arity * as the callee's type, @@ -539,11 +549,18 @@ public abstract class MethodHandle { * @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call */ + public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable; + + /** + * Temporary alias for {@link #invoke}, for backward compatibility with some versions of JSR 292. + * On some JVMs, support can be excluded by the flags {@code -XX:+UnlockExperimentalVMOptions -XX:-AllowInvokeGeneric}. + * @deprecated Will be removed for JSR 292 Proposed Final Draft. + */ public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable; /** * Performs a varargs invocation, passing the arguments in the given array - * to the method handle, as if via {@link #invokeGeneric invokeGeneric} from a call site + * to the method handle, as if via an inexact {@link #invoke invoke} from a call site * which mentions only the type {@code Object}, and whose arity is the length * of the argument array. *

    @@ -553,7 +570,7 @@ public abstract class MethodHandle { *

      *
    • Determine the length of the argument array as {@code N}. * For a null reference, {@code N=0}.
    • - *
    • Determine the generic type {@code TN} of {@code N} arguments as + *
    • Determine the general type {@code TN} of {@code N} arguments as * as {@code TN=MethodType.genericMethodType(N)}.
    • *
    • Force the original target method handle {@code MH0} to the * required type, as {@code MH1 = MH0.asType(TN)}.
    • @@ -580,7 +597,7 @@ public abstract class MethodHandle { * Object result = invoker.invokeExact(this, arguments); * *

      - * Unlike the signature polymorphic methods {@code invokeExact} and {@code invokeGeneric}, + * Unlike the signature polymorphic methods {@code invokeExact} and {@code invoke}, * {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI. * It can therefore be used as a bridge between native or reflective code and method handles. * @@ -595,11 +612,11 @@ public abstract class MethodHandle { int argc = arguments == null ? 0 : arguments.length; MethodType type = type(); if (type.parameterCount() != argc) { - // simulate invokeGeneric + // simulate invoke return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments); } if (argc <= 10) { - MethodHandle invoker = type.invokers().genericInvoker(); + MethodHandle invoker = type.invokers().generalInvoker(); switch (argc) { case 0: return invoker.invokeExact(this); case 1: return invoker.invokeExact(this, @@ -644,7 +661,7 @@ public abstract class MethodHandle { /** * Performs a varargs invocation, passing the arguments in the given array - * to the method handle, as if via {@link #invokeGeneric invokeGeneric} from a call site + * to the method handle, as if via an inexact {@link #invoke invoke} from a call site * which mentions only the type {@code Object}, and whose arity is the length * of the argument array. *

      @@ -672,9 +689,9 @@ public abstract class MethodHandle { * If the original type and new type are equal, returns {@code this}. *

      * This method provides the crucial behavioral difference between - * {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}. The two methods + * {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}. The two methods * perform the same steps when the caller's type descriptor is identical - * with the callee's, but when the types differ, {@link #invokeGeneric invokeGeneric} + * with the callee's, but when the types differ, plain {@link #invoke invoke} * also calls {@code asType} (or some internal equivalent) in order * to match up the caller's and callee's types. *

      @@ -798,7 +815,7 @@ public abstract class MethodHandle { *

      * The type and behavior of the adapter will be the same as * the type and behavior of the target, except that certain - * {@code invokeGeneric} and {@code asType} requests can lead to + * {@code invoke} and {@code asType} requests can lead to * trailing positional arguments being collected into target's * trailing parameter. * Also, the last parameter type of the adapter will be @@ -812,17 +829,17 @@ public abstract class MethodHandle { * since it accepts a whole array of indeterminate length, * rather than a fixed number of arguments.) *

      - * When called with {@link #invokeGeneric invokeGeneric}, if the caller + * When called with plain, inexact {@link #invoke invoke}, if the caller * type is the same as the adapter, the adapter invokes the target as with * {@code invokeExact}. - * (This is the normal behavior for {@code invokeGeneric} when types match.) + * (This is the normal behavior for {@code invoke} when types match.) *

      * Otherwise, if the caller and adapter arity are the same, and the * trailing parameter type of the caller is a reference type identical to * or assignable to the trailing parameter type of the adapter, * the arguments and return values are converted pairwise, * as if by {@link MethodHandles#convertArguments convertArguments}. - * (This is also normal behavior for {@code invokeGeneric} in such a case.) + * (This is also normal behavior for {@code invoke} in such a case.) *

      * Otherwise, the arities differ, or the adapter's trailing parameter * type is not assignable from the corresponding caller type. @@ -838,7 +855,7 @@ public abstract class MethodHandle { * where {@code N} is the arity of the target. * Also, there must exist conversions from the incoming arguments * to the target's arguments. - * As with other uses of {@code invokeGeneric}, if these basic + * As with other uses of plain {@code invoke}, if these basic * requirements are not fulfilled, a {@code WrongMethodTypeException} * may be thrown. *

      @@ -856,7 +873,7 @@ public abstract class MethodHandle { *

      * The behavior of {@link #asType asType} is also specialized for * variable arity adapters, to maintain the invariant that - * {@code invokeGeneric} is always equivalent to an {@code asType} + * plain, inexact {@code invoke} is always equivalent to an {@code asType} * call to adjust the target type, followed by {@code invokeExact}. * Therefore, a variable arity adapter responds * to an {@code asType} request by building a fixed arity collector, @@ -893,12 +910,12 @@ public abstract class MethodHandle { MethodHandle asList = publicLookup() .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)) .asVarargsCollector(Object[].class); -assertEquals("[]", asList.invokeGeneric().toString()); -assertEquals("[1]", asList.invokeGeneric(1).toString()); -assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString()); +assertEquals("[]", asList.invoke().toString()); +assertEquals("[1]", asList.invoke(1).toString()); +assertEquals("[two, too]", asList.invoke("two", "too").toString()); Object[] argv = { "three", "thee", "tee" }; -assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString()); -List ls = (List) asList.invokeGeneric((Object)argv); +assertEquals("[three, thee, tee]", asList.invoke(argv).toString()); +List ls = (List) asList.invoke((Object)argv); assertEquals(1, ls.size()); assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0))); * @@ -926,9 +943,9 @@ MethodHandle vamh = publicLookup() .asVarargsCollector(Object[].class); MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh); assert(vamh.type().equals(mh.type())); -assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString()); +assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString()); boolean failed = false; -try { mh.invokeGeneric(1,2,3); } +try { mh.invoke(1,2,3); } catch (WrongMethodTypeException ex) { failed = true; } assert(failed); * @@ -960,7 +977,7 @@ assert(failed); *

    • an {@code ldc} instruction of a {@code CONSTANT_MethodHandle} * which resolves to a variable arity Java method or constructor *
    - * @return true if this method handle accepts more than one arity of {@code invokeGeneric} calls + * @return true if this method handle accepts more than one arity of plain, inexact {@code invoke} calls * @see #asVarargsCollector */ public boolean isVarargsCollector() { diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java index 82d5d898638..3c7211eb920 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -313,7 +313,7 @@ class MethodHandleNatives { } /** - * The JVM wants to use a MethodType with invokeGeneric. Give the runtime fair warning. + * The JVM wants to use a MethodType with inexact invoke. Give the runtime fair warning. */ static void notifyGenericMethodType(MethodType type) { type.form().notifyGenericMethodType(); diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java index 12e36da623a..40af515771a 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java @@ -190,7 +190,7 @@ public class MethodHandles { * is not symbolically accessible from the lookup class's loader, * the lookup can still succeed. * For example, lookups for {@code MethodHandle.invokeExact} and - * {@code MethodHandle.invokeGeneric} will always succeed, regardless of requested type. + * {@code MethodHandle.invoke} will always succeed, regardless of requested type. *
  • If there is a security manager installed, it can forbid the lookup * on various grounds (see below). * By contrast, the {@code ldc} instruction is not subject to @@ -590,10 +590,10 @@ public class MethodHandles { * Because of the general equivalence between {@code invokevirtual} * instructions and method handles produced by {@code findVirtual}, * if the class is {@code MethodHandle} and the name string is - * {@code invokeExact} or {@code invokeGeneric}, the resulting + * {@code invokeExact} or {@code invoke}, the resulting * method handle is equivalent to one produced by * {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker} or - * {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker} + * {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker} * with the same {@code type} argument. * * @param refc the class or interface from which the method is accessed @@ -1148,7 +1148,7 @@ return mh1; *
  • an {@code Object[]} array containing more arguments * *

    - * The invoker will behave like a call to {@link MethodHandle#invokeGeneric invokeGeneric} with + * The invoker will behave like a call to {@link MethodHandle#invoke invoke} with * the indicated {@code type}. * That is, if the target is exactly of the given {@code type}, it will behave * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType} @@ -1166,7 +1166,7 @@ return mh1; *

    * This method is equivalent to the following code (though it may be more efficient): *

    -MethodHandle invoker = MethodHandles.genericInvoker(type);
    +MethodHandle invoker = MethodHandles.invoker(type);
     int spreadArgCount = type.parameterCount - objectArgCount;
     invoker = invoker.asSpreader(Object[].class, spreadArgCount);
     return invoker;
    @@ -1186,7 +1186,7 @@ return invoker;
     
         /**
          * Produces a special invoker method handle which can be used to
    -     * invoke any method handle of the given type, as if by {@code invokeExact}.
    +     * invoke any method handle of the given type, as if by {@link MethodHandle#invokeExact invokeExact}.
          * The resulting invoker will have a type which is
          * exactly equal to the desired type, except that it will accept
          * an additional leading argument of type {@code MethodHandle}.
    @@ -1203,7 +1203,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
          * For example, to emulate an {@code invokeExact} call to a variable method
          * handle {@code M}, extract its type {@code T},
          * look up the invoker method {@code X} for {@code T},
    -     * and call the invoker method, as {@code X.invokeGeneric(T, A...)}.
    +     * and call the invoker method, as {@code X.invoke(T, A...)}.
          * (It would not work to call {@code X.invokeExact}, since the type {@code T}
          * is unknown.)
          * If spreading, collecting, or other argument transformations are required,
    @@ -1212,7 +1212,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
          * 

    * (Note: The invoker method is not available via the Core Reflection API. * An attempt to call {@linkplain java.lang.reflect.Method#invoke Method.invoke} - * on the declared {@code invokeExact} or {@code invokeGeneric} method will raise an + * on the declared {@code invokeExact} or {@code invoke} method will raise an * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.) *

    * This method throws no reflective or security exceptions. @@ -1226,20 +1226,20 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type) /** * Produces a special invoker method handle which can be used to - * invoke any method handle of the given type, as if by {@code invokeGeneric}. + * invoke any method handle compatible with the given type, as if by {@link MethodHandle#invoke invoke}. * The resulting invoker will have a type which is * exactly equal to the desired type, except that it will accept * an additional leading argument of type {@code MethodHandle}. *

    * Before invoking its target, the invoker will apply reference casts as - * necessary and unbox and widen primitive arguments, as if by {@link #convertArguments convertArguments}. - * The return value of the invoker will be an {@code Object} reference, - * boxing a primitive value if the original type returns a primitive, - * and always null if the original type returns void. + * necessary and box, unbox, or widen primitive values, as if by {@link MethodHandle#asType asType}. + * Similarly, the return value will be converted as necessary. + * If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle}, + * the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}. *

    * This method is equivalent to the following code (though it may be more efficient): *

    -publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
    +publicLookup().findVirtual(MethodHandle.class, "invoke", type)
          * 
    *

    * This method throws no reflective or security exceptions. @@ -1247,8 +1247,17 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type) * @return a method handle suitable for invoking any method handle convertible to the given type */ static public + MethodHandle invoker(MethodType type) { + return type.invokers().generalInvoker(); + } + + /** + * Temporary alias for {@link #invoker}, for backward compatibility with some versions of JSR 292. + * @deprecated Will be removed for JSR 292 Proposed Final Draft. + */ + public static MethodHandle genericInvoker(MethodType type) { - return type.invokers().genericInvoker(); + return invoker(type); } /** @@ -2142,7 +2151,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 * the given {@code target} on the incoming arguments, * and returning or throwing whatever the {@code target} * returns or throws. The invocation will be as if by - * {@code target.invokeGeneric}. + * {@code target.invoke}. * The target's type will be checked before the * instance is created, as if by a call to {@code asType}, * which may result in a {@code WrongMethodTypeException}. diff --git a/jdk/src/share/classes/java/lang/invoke/MethodType.java b/jdk/src/share/classes/java/lang/invoke/MethodType.java index a7fa147fa32..ff3d213a341 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodType.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java @@ -39,7 +39,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * matched between a method handle and all its callers, * and the JVM's operations enforce this matching at, specifically * during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact} - * and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution + * and {@link MethodHandle#invoke MethodHandle.invoke}, and during execution * of {@code invokedynamic} instructions. *

    * The structure is a return type accompanied by any number of parameter types. @@ -294,7 +294,7 @@ class MethodType implements java.io.Serializable { * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * All parameters and the return type will be Object. * @param objectArgCount number of parameters - * @return a totally generic method type, given only its count of parameters + * @return a generally applicable method type, for all calls of the given argument count * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 * @see #genericMethodType(int, boolean) */ diff --git a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java index db73b24d067..92a9031e0cb 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java @@ -59,7 +59,7 @@ class MethodTypeForm { /*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with /*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many /*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly - /*lazy*/ MethodHandle genericInvoker; // hook for invokeGeneric + /*lazy*/ MethodHandle genericInvoker; // hook for inexact invoke public MethodType erasedType() { return erasedType; @@ -460,9 +460,9 @@ class MethodTypeForm { if (genericInvoker != null) return; try { // Trigger adapter creation. - genericInvoker = InvokeGeneric.genericInvokerOf(erasedType); + genericInvoker = InvokeGeneric.generalInvokerOf(erasedType); } catch (Exception ex) { - Error err = new InternalError("Exception while resolving invokeGeneric"); + Error err = new InternalError("Exception while resolving inexact invoke"); err.initCause(ex); throw err; } diff --git a/jdk/src/share/classes/java/lang/invoke/package-info.java b/jdk/src/share/classes/java/lang/invoke/package-info.java index b6a1992dca8..8ec18c461fe 100644 --- a/jdk/src/share/classes/java/lang/invoke/package-info.java +++ b/jdk/src/share/classes/java/lang/invoke/package-info.java @@ -185,7 +185,7 @@ * The method handle constant produced for such a method behaves as if * it were created by {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector}. * In other words, the constant method handle will exhibit variable arity, - * when invoked via {@code invokeGeneric}. + * when invoked via {@code MethodHandle.invoke}. * On the other hand, its behavior with respect to {@code invokeExact} will be the same * as if the {@code varargs} bit were not set. *

    @@ -243,7 +243,7 @@ *

  • optionally, one or more additional static arguments
  • * * The method handle is then applied to the other values as if by - * {@link java.lang.invoke.MethodHandle#invokeGeneric invokeGeneric}. + * {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}. * The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass). * The type of the call site's target must be exactly equal to the type * derived from the dynamic call site's type descriptor and passed to @@ -251,7 +251,7 @@ * The call site then becomes permanently linked to the dynamic call site. *

    * As long as each bootstrap method can be correctly invoked - * by invokeGeneric, its detailed type is arbitrary. + * by MethodHandle.invoke, its detailed type is arbitrary. * For example, the first argument could be {@code Object} * instead of {@code MethodHandles.Lookup}, and the return type * could also be {@code Object} instead of {@code CallSite}. @@ -272,7 +272,7 @@ * (i.e., a {@code CONSTANT_Class}, {@code CONSTANT_MethodType}, * or {@code CONSTANT_MethodHandle} argument cannot be linked)

  • *
  • the bootstrap method has the wrong arity, - * causing {@code invokeGeneric} to throw {@code WrongMethodTypeException}
  • + * causing {@code MethodHandle.invoke} to throw {@code WrongMethodTypeException} *
  • the bootstrap method has a wrong argument or return type
  • *
  • the bootstrap method invocation completes abnormally
  • *
  • the result from the bootstrap invocation is not a reference to @@ -381,10 +381,10 @@ * those values will be passed as additional arguments to the method handle. * (Note that because there is a limit of 255 arguments to any method, * at most 252 extra arguments can be supplied.) - * The bootstrap method will be invoked as if by either {@code invokeGeneric} + * The bootstrap method will be invoked as if by either {@code MethodHandle.invoke} * or {@code invokeWithArguments}. (There is no way to tell the difference.) *

    - * The normal argument conversion rules for {@code invokeGeneric} apply to all stacked arguments. + * The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments. * For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion. * If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set), * then some or all of the arguments specified here may be collected into a trailing array parameter. @@ -419,8 +419,8 @@ * For example, the fourth argument could be {@code MethodHandle}, * if that is the type of the corresponding constant in * the {@code CONSTANT_InvokeDynamic} entry. - * In that case, the {@code invokeGeneric} call will pass the extra method handle - * constant as an {@code Object}, but the type matching machinery of {@code invokeGeneric} + * In that case, the {@code MethodHandle.invoke} call will pass the extra method handle + * constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke} * will cast the reference back to {@code MethodHandle} before invoking the bootstrap method. * (If a string constant were passed instead, by badly generated code, that cast would then fail, * resulting in a {@code BootstrapMethodError}.) diff --git a/jdk/test/java/lang/invoke/InvokeGenericTest.java b/jdk/test/java/lang/invoke/InvokeGenericTest.java index 2da9634c464..792f51833f9 100644 --- a/jdk/test/java/lang/invoke/InvokeGenericTest.java +++ b/jdk/test/java/lang/invoke/InvokeGenericTest.java @@ -24,7 +24,7 @@ */ /* @test - * @summary unit tests for java.lang.invoke.MethodHandle.invokeGeneric + * @summary unit tests for java.lang.invoke.MethodHandle.invoke * @compile -target 7 InvokeGenericTest.java * @run junit/othervm test.java.lang.invoke.InvokeGenericTest */ @@ -350,6 +350,18 @@ public class InvokeGenericTest { String[] args = { "one", "two" }; MethodHandle mh = callable(Object.class, String.class); Object res; List resl; + res = resl = (List) mh.invoke((String)args[0], (Object)args[1]); + //System.out.println(res); + assertEquals(Arrays.asList(args), res); + } + + @Test + public void testAlternateName() throws Throwable { + startTest("testAlternateName"); + countTest(); + String[] args = { "one", "two" }; + MethodHandle mh = callable(Object.class, String.class); + Object res; List resl; res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]); //System.out.println(res); assertEquals(Arrays.asList(args), res); @@ -388,15 +400,15 @@ public class InvokeGenericTest { try { switch (args.length) { case 0: - junk = target.invokeGeneric(); break; + junk = target.invoke(); break; case 1: - junk = target.invokeGeneric(args[0]); break; + junk = target.invoke(args[0]); break; case 2: - junk = target.invokeGeneric(args[0], args[1]); break; + junk = target.invoke(args[0], args[1]); break; case 3: - junk = target.invokeGeneric(args[0], args[1], args[2]); break; + junk = target.invoke(args[0], args[1], args[2]); break; case 4: - junk = target.invokeGeneric(args[0], args[1], args[2], args[3]); break; + junk = target.invoke(args[0], args[1], args[2], args[3]); break; default: junk = target.invokeWithArguments(args); break; } @@ -451,7 +463,7 @@ public class InvokeGenericTest { startTest("testReferenceConversions"); toString_MH = LOOKUP. findVirtual(Object.class, "toString", MethodType.methodType(String.class)); - String[] args = { "one", "two" }; + Object[] args = { "one", "two" }; for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) { testReferenceConversions(type, args); } @@ -463,7 +475,7 @@ public class InvokeGenericTest { MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList()); mh = MethodHandles.foldArguments(tsdrop, mh); mh = mh.asType(type); - Object res = mh.invokeGeneric((String)args[0], (Object)args[1]); + Object res = mh.invoke((String)args[0], (Object)args[1]); //System.out.println(res); assertEquals(Arrays.asList(args).toString(), res); } @@ -473,10 +485,10 @@ public class InvokeGenericTest { public void testBoxConversions() throws Throwable { startTest("testBoxConversions"); countTest(); - Integer[] args = { 1, 2 }; + Object[] args = { 1, 2 }; MethodHandle mh = callable(Object.class, int.class); Object res; List resl; - res = resl = (List) mh.invokeGeneric((int)args[0], (Object)args[1]); + res = resl = (List) mh.invoke((int)args[0], (Object)args[1]); //System.out.println(res); assertEquals(Arrays.asList(args), res); } diff --git a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java index 7810399c62d..9f68a20061f 100644 --- a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java +++ b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java @@ -170,8 +170,8 @@ assert(s.equals("savvy")); mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); -x = mh.invokeGeneric("one", "two"); -// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; +x = mh.invoke("one", "two"); +// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; assert(x.equals(java.util.Arrays.asList("one","two"))); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); @@ -199,12 +199,12 @@ mh.invokeExact(System.out, "Hello, world."); MethodHandle asList = publicLookup() .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)) .asVarargsCollector(Object[].class); -assertEquals("[]", asList.invokeGeneric().toString()); -assertEquals("[1]", asList.invokeGeneric(1).toString()); -assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString()); +assertEquals("[]", asList.invoke().toString()); +assertEquals("[1]", asList.invoke(1).toString()); +assertEquals("[two, too]", asList.invoke("two", "too").toString()); Object[] argv = { "three", "thee", "tee" }; -assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString()); -List ls = (List) asList.invokeGeneric((Object)argv); +assertEquals("[three, thee, tee]", asList.invoke(argv).toString()); +List ls = (List) asList.invoke((Object)argv); assertEquals(1, ls.size()); assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0))); }} @@ -218,9 +218,9 @@ MethodHandle vamh = publicLookup() .asVarargsCollector(Object[].class); MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh); assert(vamh.type().equals(mh.type())); -assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString()); +assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString()); boolean failed = false; -try { mh.invokeGeneric(1,2,3); } +try { mh.invoke(1,2,3); } catch (WrongMethodTypeException ex) { failed = true; } assert(failed); {} diff --git a/jdk/test/java/lang/invoke/MethodHandlesTest.java b/jdk/test/java/lang/invoke/MethodHandlesTest.java index 427229de7c3..0ab9f192a24 100644 --- a/jdk/test/java/lang/invoke/MethodHandlesTest.java +++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java @@ -1780,7 +1780,7 @@ public class MethodHandlesTest { assertCalled("invokee", args); // generic invoker countTest(); - inv = MethodHandles.genericInvoker(type); + inv = MethodHandles.invoker(type); if (nargs <= 3) { calledLog.clear(); switch (nargs) { From 9a58e303d715b1c44a8d78b3832f5788b23c4ca9 Mon Sep 17 00:00:00 2001 From: John R Rose Date: Thu, 12 May 2011 19:27:49 -0700 Subject: [PATCH 51/65] 6939861: JVM should handle more conversion operations Integrate JDK code with JVM-supplied ricochet frames. Reviewed-by: never, twisti --- .../java/lang/invoke/AdapterMethodHandle.java | 321 ++++++-- .../java/lang/invoke/FilterGeneric.java | 4 + .../java/lang/invoke/FilterOneArgument.java | 4 + .../classes/java/lang/invoke/FromGeneric.java | 12 +- .../classes/java/lang/invoke/Invokers.java | 4 +- .../classes/java/lang/invoke/MemberName.java | 2 +- .../java/lang/invoke/MethodHandle.java | 31 +- .../java/lang/invoke/MethodHandleImpl.java | 581 ++++++++------- .../java/lang/invoke/MethodHandleNatives.java | 85 ++- .../java/lang/invoke/MethodHandleStatics.java | 20 +- .../java/lang/invoke/MethodHandles.java | 98 ++- .../classes/java/lang/invoke/MethodType.java | 39 +- .../java/lang/invoke/MethodTypeForm.java | 1 + .../java/lang/invoke/SpreadGeneric.java | 4 + .../classes/java/lang/invoke/ToGeneric.java | 18 +- .../sun/invoke/util/ValueConversions.java | 693 ++++++++++++++---- .../classes/sun/invoke/util/VerifyType.java | 11 +- .../classes/sun/invoke/util/Wrapper.java | 137 +++- .../java/lang/invoke/6998541/Test6998541.java | 513 +++++++++++++ .../java/lang/invoke/InvokeGenericTest.java | 22 +- .../java/lang/invoke/MethodHandlesTest.java | 170 +++-- jdk/test/java/lang/invoke/RicochetTest.java | 582 +++++++++++++++ .../sun/invoke/util/ValueConversionsTest.java | 448 +++++++++++ 23 files changed, 3165 insertions(+), 635 deletions(-) create mode 100644 jdk/test/java/lang/invoke/6998541/Test6998541.java create mode 100644 jdk/test/java/lang/invoke/RicochetTest.java create mode 100644 jdk/test/sun/invoke/util/ValueConversionsTest.java diff --git a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java b/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java index a47b38f8bb3..a77c904eb0e 100644 --- a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java +++ b/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java @@ -27,6 +27,7 @@ package java.lang.invoke; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; +import sun.invoke.util.ValueConversions; import java.util.Arrays; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; @@ -55,29 +56,35 @@ class AdapterMethodHandle extends BoundMethodHandle { this(target, newType, conv, null); } + int getConversion() { return conversion; } + // TO DO: When adapting another MH with a null conversion, clone // the target and change its type, instead of adding another layer. /** Can a JVM-level adapter directly implement the proposed * argument conversions, as if by MethodHandles.convertArguments? */ - static boolean canPairwiseConvert(MethodType newType, MethodType oldType) { + static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) { // same number of args, of course int len = newType.parameterCount(); if (len != oldType.parameterCount()) return false; - // Check return type. (Not much can be done with it.) + // Check return type. Class exp = newType.returnType(); Class ret = oldType.returnType(); - if (!VerifyType.isNullConversion(ret, exp)) - return false; + if (!VerifyType.isNullConversion(ret, exp)) { + if (!convOpSupported(OP_COLLECT_ARGS)) + return false; + if (!canConvertArgument(ret, exp, level)) + return false; + } // Check args pairwise. for (int i = 0; i < len; i++) { Class src = newType.parameterType(i); // source type Class dst = oldType.parameterType(i); // destination type - if (!canConvertArgument(src, dst)) + if (!canConvertArgument(src, dst, level)) return false; } @@ -87,11 +94,14 @@ class AdapterMethodHandle extends BoundMethodHandle { /** Can a JVM-level adapter directly implement the proposed * argument conversion, as if by MethodHandles.convertArguments? */ - static boolean canConvertArgument(Class src, Class dst) { + static boolean canConvertArgument(Class src, Class dst, int level) { // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes, // so we don't need to repeat so much decision making. if (VerifyType.isNullConversion(src, dst)) { return true; + } else if (convOpSupported(OP_COLLECT_ARGS)) { + // If we can build filters, we can convert anything to anything. + return true; } else if (src.isPrimitive()) { if (dst.isPrimitive()) return canPrimCast(src, dst); @@ -99,7 +109,7 @@ class AdapterMethodHandle extends BoundMethodHandle { return canBoxArgument(src, dst); } else { if (dst.isPrimitive()) - return canUnboxArgument(src, dst); + return canUnboxArgument(src, dst, level); else return true; // any two refs can be interconverted } @@ -109,21 +119,20 @@ class AdapterMethodHandle extends BoundMethodHandle { * Create a JVM-level adapter method handle to conform the given method * handle to the similar newType, using only pairwise argument conversions. * For each argument, convert incoming argument to the exact type needed. - * Only null conversions are allowed on the return value (until - * the JVM supports ricochet adapters). - * The argument conversions allowed are casting, unboxing, + * The argument conversions allowed are casting, boxing and unboxing, * integral widening or narrowing, and floating point widening or narrowing. * @param newType required call type * @param target original method handle + * @param level which strength of conversion is allowed * @return an adapter to the original handle with the desired new type, * or the original target if the types are already identical * or null if the adaptation cannot be made */ - static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target) { + static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level) { MethodType oldType = target.type(); if (newType == oldType) return target; - if (!canPairwiseConvert(newType, oldType)) + if (!canPairwiseConvert(newType, oldType, level)) return null; // (after this point, it is an assertion error to fail to convert) @@ -138,9 +147,14 @@ class AdapterMethodHandle extends BoundMethodHandle { break; } } + + Class needReturn = newType.returnType(); + Class haveReturn = oldType.returnType(); + boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn); + // Now build a chain of one or more adapters. - MethodHandle adapter = target; - MethodType midType = oldType.changeReturnType(newType.returnType()); + MethodHandle adapter = target, adapter2; + MethodType midType = oldType; for (int i = 0; i <= lastConv; i++) { Class src = newType.parameterType(i); // source type Class dst = midType.parameterType(i); // destination type @@ -149,22 +163,23 @@ class AdapterMethodHandle extends BoundMethodHandle { continue; } // Work the current type backward toward the desired caller type: - if (i != lastConv) { - midType = midType.changeParameterType(i, src); - } else { + midType = midType.changeParameterType(i, src); + if (i == lastConv) { // When doing the last (or only) real conversion, // force all remaining null conversions to happen also. - assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src))); - midType = newType; + MethodType lastMidType = newType; + if (retConv) lastMidType = lastMidType.changeReturnType(haveReturn); + assert(VerifyType.isNullConversion(lastMidType, midType)); + midType = lastMidType; } // Tricky case analysis follows. // It parallels canConvertArgument() above. if (src.isPrimitive()) { if (dst.isPrimitive()) { - adapter = makePrimCast(midType, adapter, i, dst); + adapter2 = makePrimCast(midType, adapter, i, dst); } else { - adapter = makeBoxArgument(midType, adapter, i, dst); + adapter2 = makeBoxArgument(midType, adapter, i, src); } } else { if (dst.isPrimitive()) { @@ -174,29 +189,53 @@ class AdapterMethodHandle extends BoundMethodHandle { // conversions supported by reflect.Method.invoke. // Those conversions require a big nest of if/then/else logic, // which we prefer to make a user responsibility. - adapter = makeUnboxArgument(midType, adapter, i, dst); + adapter2 = makeUnboxArgument(midType, adapter, i, dst, level); } else { // Simple reference conversion. // Note: Do not check for a class hierarchy relation // between src and dst. In all cases a 'null' argument // will pass the cast conversion. - adapter = makeCheckCast(midType, adapter, i, dst); + adapter2 = makeCheckCast(midType, adapter, i, dst); } } - assert(adapter != null); - assert(adapter.type() == midType); + assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType); + assert(adapter2.type() == midType); + adapter = adapter2; + } + if (retConv) { + adapter2 = makeReturnConversion(adapter, haveReturn, needReturn); + assert(adapter2 != null); + adapter = adapter2; } if (adapter.type() != newType) { // Only trivial conversions remain. - adapter = makeRetypeOnly(newType, adapter); - assert(adapter != null); + adapter2 = makeRetypeOnly(newType, adapter); + assert(adapter2 != null); + adapter = adapter2; // Actually, that's because there were no non-trivial ones: - assert(lastConv == -1); + assert(lastConv == -1 || retConv); } assert(adapter.type() == newType); return adapter; } + private static MethodHandle makeReturnConversion(MethodHandle target, Class haveReturn, Class needReturn) { + MethodHandle adjustReturn; + if (haveReturn == void.class) { + // synthesize a zero value for the given void + Object zero = Wrapper.forBasicType(needReturn).zero(); + adjustReturn = MethodHandles.constant(needReturn, zero); + } else { + MethodType needConversion = MethodType.methodType(needReturn, haveReturn); + adjustReturn = MethodHandles.identity(needReturn).asType(needConversion); + } + if (!canCollectArguments(adjustReturn.type(), target.type(), 0, false)) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + throw new InternalError("NYI"); + } + return makeCollectArguments(adjustReturn, target, 0, false); + } + /** * Create a JVM-level adapter method handle to permute the arguments * of the given method. @@ -224,7 +263,7 @@ class AdapterMethodHandle extends BoundMethodHandle { if (argumentMap.length != oldType.parameterCount()) throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap)); if (nullPermutation) { - MethodHandle res = makePairwiseConvert(newType, target); + MethodHandle res = makePairwiseConvert(newType, target, 0); // well, that was easy if (res == null) throw newIllegalArgumentException("cannot convert pairwise: "+newType); @@ -310,11 +349,25 @@ class AdapterMethodHandle extends BoundMethodHandle { return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT; } + static int extractStackMove(int convOp) { + int spChange = convOp >> CONV_STACK_MOVE_SHIFT; + return spChange / MethodHandleNatives.JVM_STACK_MOVE_UNIT; + } + + static int extractStackMove(MethodHandle target) { + if (target instanceof AdapterMethodHandle) { + AdapterMethodHandle amh = (AdapterMethodHandle) target; + return extractStackMove(amh.getConversion()); + } else { + return 0; + } + } + /** Construct an adapter conversion descriptor for a single-argument conversion. */ private static long makeConv(int convOp, int argnum, int src, int dest) { - assert(src == (src & 0xF)); - assert(dest == (dest & 0xF)); - assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF); + assert(src == (src & CONV_TYPE_MASK)); + assert(dest == (dest & CONV_TYPE_MASK)); + assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS); int stackMove = type2size(dest) - type2size(src); return ((long) argnum << 32 | (long) convOp << CONV_OP_SHIFT | @@ -323,11 +376,10 @@ class AdapterMethodHandle extends BoundMethodHandle { insertStackMove(stackMove) ); } - private static long makeConv(int convOp, int argnum, int stackMove) { - assert(convOp >= OP_DUP_ARGS && convOp <= OP_SPREAD_ARGS); + private static long makeDupConv(int convOp, int argnum, int stackMove) { + // simple argument motion, requiring one slot to specify + assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS); byte src = 0, dest = 0; - if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS) - src = dest = T_OBJECT; return ((long) argnum << 32 | (long) convOp << CONV_OP_SHIFT | (int) src << CONV_SRC_TYPE_SHIFT | @@ -336,7 +388,8 @@ class AdapterMethodHandle extends BoundMethodHandle { ); } private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) { - assert(convOp >= OP_SWAP_ARGS && convOp <= OP_ROT_ARGS); + // more complex argument motion, requiring two slots to specify + assert(convOp == OP_SWAP_ARGS || convOp == OP_ROT_ARGS); return ((long) srcArg << 32 | (long) convOp << CONV_OP_SHIFT | (int) type << CONV_SRC_TYPE_SHIFT | @@ -344,6 +397,18 @@ class AdapterMethodHandle extends BoundMethodHandle { (int) destSlot << CONV_VMINFO_SHIFT ); } + private static long makeSpreadConv(int convOp, int argnum, int src, int dest, int stackMove) { + // spreading or collecting, at a particular slot location + assert(convOp == OP_SPREAD_ARGS || convOp == OP_COLLECT_ARGS || convOp == OP_FOLD_ARGS); + // src = spread ? T_OBJECT (for array) : common type of collected args (else void) + // dest = spread ? element type of array : result type of collector (can be void) + return ((long) argnum << 32 | + (long) convOp << CONV_OP_SHIFT | + (int) src << CONV_SRC_TYPE_SHIFT | + (int) dest << CONV_DEST_TYPE_SHIFT | + insertStackMove(stackMove) + ); + } private static long makeConv(int convOp) { assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW); return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero @@ -570,14 +635,10 @@ class AdapterMethodHandle extends BoundMethodHandle { static boolean canPrimCast(Class src, Class dst) { if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) { return false; - } else if (Wrapper.forPrimitiveType(dst).isFloating()) { - // both must be floating types - return Wrapper.forPrimitiveType(src).isFloating(); } else { - // both are integral, and all combinations work fine - assert(Wrapper.forPrimitiveType(src).isIntegral() && - Wrapper.forPrimitiveType(dst).isIntegral()); - return true; + boolean sflt = Wrapper.forPrimitiveType(src).isFloating(); + boolean dflt = Wrapper.forPrimitiveType(dst).isFloating(); + return !(sflt | dflt); // no float support at present } } @@ -589,6 +650,29 @@ class AdapterMethodHandle extends BoundMethodHandle { */ static MethodHandle makePrimCast(MethodType newType, MethodHandle target, int arg, Class convType) { + Class src = newType.parameterType(arg); + if (canPrimCast(src, convType)) + return makePrimCastOnly(newType, target, arg, convType); + Class dst = convType; + boolean sflt = Wrapper.forPrimitiveType(src).isFloating(); + boolean dflt = Wrapper.forPrimitiveType(dst).isFloating(); + if (sflt | dflt) { + MethodHandle convMethod; + if (sflt) + convMethod = ((src == double.class) + ? ValueConversions.convertFromDouble(dst) + : ValueConversions.convertFromFloat(dst)); + else + convMethod = ((dst == double.class) + ? ValueConversions.convertToDouble(src) + : ValueConversions.convertToFloat(src)); + long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst)); + return new AdapterMethodHandle(target, newType, conv, convMethod); + } + throw new InternalError("makePrimCast"); + } + static MethodHandle makePrimCastOnly(MethodType newType, MethodHandle target, + int arg, Class convType) { MethodType oldType = target.type(); if (!canPrimCast(newType, oldType, arg, convType)) return null; @@ -602,7 +686,7 @@ class AdapterMethodHandle extends BoundMethodHandle { * The convType is the unboxed type; it can be either a primitive or wrapper. */ static boolean canUnboxArgument(MethodType newType, MethodType targetType, - int arg, Class convType) { + int arg, Class convType, int level) { if (!convOpSupported(OP_REF_TO_PRIM)) return false; Class src = newType.parameterType(arg); Class dst = targetType.parameterType(arg); @@ -616,21 +700,31 @@ class AdapterMethodHandle extends BoundMethodHandle { return (diff == arg+1); // arg is sole non-trivial diff } /** Can an primitive unboxing adapter validly convert src to dst? */ - static boolean canUnboxArgument(Class src, Class dst) { - return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive()); + static boolean canUnboxArgument(Class src, Class dst, int level) { + assert(dst.isPrimitive()); + // if we have JVM support for boxing, we can also do complex unboxing + if (convOpSupported(OP_PRIM_TO_REF)) return true; + Wrapper dw = Wrapper.forPrimitiveType(dst); + // Level 0 means cast and unbox. This works on any reference. + if (level == 0) return !src.isPrimitive(); + assert(level >= 0 && level <= 2); + // Levels 1 and 2 allow widening and/or narrowing conversions. + // These are not supported directly by the JVM. + // But if the input reference is monomorphic, we can do it. + return dw.wrapperType() == src; } /** Factory method: Unbox the given argument. * Return null if this cannot be done. */ static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target, - int arg, Class convType) { + int arg, Class convType, int level) { MethodType oldType = target.type(); Class src = newType.parameterType(arg); Class dst = oldType.parameterType(arg); Class boxType = Wrapper.asWrapperType(convType); Class primType = Wrapper.asPrimitiveType(convType); - if (!canUnboxArgument(newType, oldType, arg, convType)) + if (!canUnboxArgument(newType, oldType, arg, convType, level)) return null; MethodType castDone = newType; if (!VerifyType.isNullConversion(src, boxType)) @@ -642,19 +736,46 @@ class AdapterMethodHandle extends BoundMethodHandle { return makeCheckCast(newType, adapter, arg, boxType); } + /** Can a boxing conversion validly convert src to dst? */ + static boolean canBoxArgument(MethodType newType, MethodType targetType, + int arg, Class convType) { + if (!convOpSupported(OP_PRIM_TO_REF)) return false; + Class src = newType.parameterType(arg); + Class dst = targetType.parameterType(arg); + Class boxType = Wrapper.asWrapperType(convType); + convType = Wrapper.asPrimitiveType(convType); + if (!canCheckCast(boxType, dst) + || boxType == convType + || !VerifyType.isNullConversion(src, convType)) + return false; + int diff = diffTypes(newType, targetType, false); + return (diff == arg+1); // arg is sole non-trivial diff + } + /** Can an primitive boxing adapter validly convert src to dst? */ static boolean canBoxArgument(Class src, Class dst) { if (!convOpSupported(OP_PRIM_TO_REF)) return false; - throw new UnsupportedOperationException("NYI"); + return (src.isPrimitive() && !dst.isPrimitive()); } - /** Factory method: Unbox the given argument. + /** Factory method: Box the given argument. * Return null if this cannot be done. */ static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target, int arg, Class convType) { - // this is difficult to do in the JVM because it must GC - return null; + MethodType oldType = target.type(); + Class src = newType.parameterType(arg); + Class dst = oldType.parameterType(arg); + Class boxType = Wrapper.asWrapperType(convType); + Class primType = Wrapper.asPrimitiveType(convType); + if (!canBoxArgument(newType, oldType, arg, convType)) { + return null; + } + if (!VerifyType.isNullConversion(boxType, dst)) + target = makeCheckCast(oldType.changeParameterType(arg, boxType), target, arg, dst); + MethodHandle boxerMethod = ValueConversions.box(Wrapper.forPrimitiveType(primType)); + long conv = makeConv(OP_PRIM_TO_REF, arg, basicType(primType), T_OBJECT); + return new AdapterMethodHandle(target, newType, conv, boxerMethod); } /** Can an adapter simply drop arguments to convert the target to newType? */ @@ -699,7 +820,7 @@ class AdapterMethodHandle extends BoundMethodHandle { int slotCount = keep1InSlot - dropSlot; assert(slotCount >= dropArgCount); assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount()); - long conv = makeConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount); + long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount); return new AdapterMethodHandle(target, newType, conv); } @@ -739,7 +860,7 @@ class AdapterMethodHandle extends BoundMethodHandle { int keep1InSlot = newType.parameterSlotDepth(dupArgPos); int slotCount = keep1InSlot - dupSlot; assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount()); - long conv = makeConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount); + long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount); return new AdapterMethodHandle(target, newType, conv); } @@ -900,7 +1021,7 @@ class AdapterMethodHandle extends BoundMethodHandle { for (int i = 0; i < spreadArgCount; i++) { Class src = VerifyType.spreadArgElementType(spreadArgType, i); Class dst = targetType.parameterType(spreadArgPos + i); - if (src == null || !VerifyType.isNullConversion(src, dst)) + if (src == null || !canConvertArgument(src, dst, 1)) return false; } return true; @@ -910,24 +1031,100 @@ class AdapterMethodHandle extends BoundMethodHandle { /** Factory method: Spread selected argument. */ static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target, Class spreadArgType, int spreadArgPos, int spreadArgCount) { + // FIXME: Get rid of newType; derive new arguments from structure of spreadArgType MethodType targetType = target.type(); if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount)) return null; + // dest is not significant; remove? + int dest = T_VOID; + for (int i = 0; i < spreadArgCount; i++) { + Class arg = VerifyType.spreadArgElementType(spreadArgType, i); + if (arg == null) arg = Object.class; + int dest2 = basicType(arg); + if (dest == T_VOID) dest = dest2; + else if (dest != dest2) dest = T_VOID; + if (dest == T_VOID) break; + targetType = targetType.changeParameterType(spreadArgPos + i, arg); + } + target = target.asType(targetType); + int arrayArgSize = 1; // always a reference // in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ] // out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ] int keep2OutPos = spreadArgPos + spreadArgCount; - int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); - int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); - int slotCount = keep1OutSlot - spreadSlot; - assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+1)); + int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); // leading edge of |spread...| + int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); // trailing edge of |spread...| + assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize)); + int slotCount = keep1OutSlot - spreadSlot; // slots in |spread...| assert(slotCount >= spreadArgCount); - long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, slotCount-1); + int stackMove = - arrayArgSize + slotCount; // pop array, push N slots + long conv = makeSpreadConv(OP_SPREAD_ARGS, spreadArgPos, T_OBJECT, dest, stackMove); MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType); assert(res.type().parameterType(spreadArgPos) == spreadArgType); return res; } - // TO DO: makeCollectArguments, makeFlyby, makeRicochet + /** Can an adapter collect a series of arguments, replacing them by zero or one results? */ + static boolean canCollectArguments(MethodType targetType, + MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) { + if (!convOpSupported(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS)) return false; + int collectArgCount = collectorType.parameterCount(); + Class rtype = collectorType.returnType(); + assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype) + // [(Object)Object[], (Object[])Object[], 0, 1] + : Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount) + ; + return true; + } + + /** Factory method: Collect or filter selected argument(s). */ + static MethodHandle makeCollectArguments(MethodHandle target, + MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) { + assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs)); + MethodType targetType = target.type(); + MethodType collectorType = collector.type(); + int collectArgCount = collectorType.parameterCount(); + Class collectValType = collectorType.returnType(); + int collectValCount = (collectValType == void.class ? 0 : 1); + int collectValSlots = collectorType.returnSlotCount(); + MethodType newType = targetType + .dropParameterTypes(collectArgPos, collectArgPos+collectValCount); + if (!retainOriginalArgs) { + newType = newType + .insertParameterTypes(collectArgPos, collectorType.parameterList()); + } else { + // parameter types at the fold point must be the same + assert(diffParamTypes(newType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0) + : Arrays.asList(target, collector, collectArgPos, retainOriginalArgs); + } + // in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ] + // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ] + // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ] + int keep2InPos = collectArgPos + collectArgCount; + int keep1InSlot = newType.parameterSlotDepth(collectArgPos); // leading edge of |collect...| + int collectSlot = newType.parameterSlotDepth(keep2InPos); // trailing edge of |collect...| + int slotCount = keep1InSlot - collectSlot; // slots in |collect...| + assert(slotCount >= collectArgCount); + assert(collectSlot == targetType.parameterSlotDepth( + collectArgPos + collectValCount + (retainOriginalArgs ? collectArgCount : 0) )); + int dest = basicType(collectValType); + int src = T_VOID; + // src is not significant; remove? + for (int i = 0; i < collectArgCount; i++) { + int src2 = basicType(collectorType.parameterType(i)); + if (src == T_VOID) src = src2; + else if (src != src2) src = T_VOID; + if (src == T_VOID) break; + } + int stackMove = collectValSlots; // push 0..2 results + if (!retainOriginalArgs) stackMove -= slotCount; // pop N arguments + int lastCollectArg = keep2InPos-1; + long conv = makeSpreadConv(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS, + lastCollectArg, src, dest, stackMove); + MethodHandle res = new AdapterMethodHandle(target, newType, conv, collector); + assert(res.type().parameterList().subList(collectArgPos, collectArgPos+collectArgCount) + .equals(collector.type().parameterList())); + return res; + } @Override public String toString() { diff --git a/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java b/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java index 6c3395002f5..769289710d9 100644 --- a/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java +++ b/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java @@ -61,6 +61,10 @@ class FilterGeneric { return ad; } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) { Adapter ad = getAdapter(kind, pos); return ad.makeInstance(ad.prototypeEntryPoint(), filter, target); diff --git a/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java b/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java index 64a9f072797..f52c8c06f0a 100644 --- a/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java +++ b/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java @@ -67,6 +67,10 @@ class FilterOneArgument extends BoundMethodHandle { this.target = target; } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + public static MethodHandle make(MethodHandle filter, MethodHandle target) { if (filter == null) return target; if (target == null) return filter; diff --git a/jdk/src/share/classes/java/lang/invoke/FromGeneric.java b/jdk/src/share/classes/java/lang/invoke/FromGeneric.java index 9a48c09a35d..1e035a46fb1 100644 --- a/jdk/src/share/classes/java/lang/invoke/FromGeneric.java +++ b/jdk/src/share/classes/java/lang/invoke/FromGeneric.java @@ -98,6 +98,10 @@ class FromGeneric { this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0); } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + /** * The typed target will be called according to targetType. * The adapter code will in fact see the raw result from internalType, @@ -112,10 +116,10 @@ class FromGeneric { assert(iret == Object.class); return ValueConversions.identity(); } else if (wrap.primitiveType() == iret) { - return ValueConversions.box(wrap, false); + return ValueConversions.box(wrap); } else { assert(tret == double.class ? iret == long.class : iret == int.class); - return ValueConversions.boxRaw(wrap, false); + return ValueConversions.boxRaw(wrap); } } @@ -135,7 +139,7 @@ class FromGeneric { MethodType fixArgsType = internalType.changeReturnType(targetType.returnType()); MethodHandle fixArgs = MethodHandleImpl.convertArguments( invoker, Invokers.invokerType(fixArgsType), - invoker.type(), null); + invoker.type(), 0); if (fixArgs == null) throw new InternalError("bad fixArgs"); // reinterpret the calling sequence as raw: @@ -230,7 +234,7 @@ class FromGeneric { } static Adapter buildAdapterFromBytecodes(MethodType internalType) { - throw new UnsupportedOperationException("NYI"); + throw new UnsupportedOperationException("NYI "+internalType); } /** diff --git a/jdk/src/share/classes/java/lang/invoke/Invokers.java b/jdk/src/share/classes/java/lang/invoke/Invokers.java index c142224bd4a..7bf134cc858 100644 --- a/jdk/src/share/classes/java/lang/invoke/Invokers.java +++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java @@ -82,7 +82,7 @@ class Invokers { MethodHandle invoker = generalInvoker; if (invoker != null) return invoker; MethodType generalType = targetType.generic(); - invoker = MethodHandles.convertArguments(invoker1, invokerType(generalType)); + invoker = invoker1.asType(invokerType(generalType)); generalInvoker = invoker; return invoker; } @@ -95,7 +95,7 @@ class Invokers { if (erasedType == targetType.generic()) invoker = generalInvoker(); else - invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType)); + invoker = invoker1.asType(invokerType(erasedType)); erasedInvoker = invoker; return invoker; } diff --git a/jdk/src/share/classes/java/lang/invoke/MemberName.java b/jdk/src/share/classes/java/lang/invoke/MemberName.java index 0300fe758cc..c61336d2af1 100644 --- a/jdk/src/share/classes/java/lang/invoke/MemberName.java +++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java @@ -525,7 +525,7 @@ import static java.lang.invoke.MethodHandleStatics.*; /** A factory type for resolving member names with the help of the VM. * TBD: Define access-safe public constructors for this factory. */ - public static class Factory { + /*non-public*/ static class Factory { private Factory() { } // singleton pattern static Factory INSTANCE = new Factory(); diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java index 31cd782e9ec..e6424f1516f 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java @@ -26,6 +26,7 @@ package java.lang.invoke; +import sun.invoke.util.ValueConversions; import static java.lang.invoke.MethodHandleStatics.*; /** @@ -706,6 +707,9 @@ public abstract class MethodHandle { * @see MethodHandles#convertArguments */ public MethodHandle asType(MethodType newType) { + if (!type.isConvertibleTo(newType)) { + throw new WrongMethodTypeException("cannot convert "+type+" to "+newType); + } return MethodHandles.convertArguments(this, newType); } @@ -748,13 +752,9 @@ public abstract class MethodHandle { public MethodHandle asSpreader(Class arrayType, int arrayLength) { Class arrayElement = arrayType.getComponentType(); if (arrayElement == null) throw newIllegalArgumentException("not an array type"); - MethodType oldType = type(); - int nargs = oldType.parameterCount(); + int nargs = type().parameterCount(); if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length"); - int keepPosArgs = nargs - arrayLength; - MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs); - newType = newType.insertParameterTypes(keepPosArgs, arrayType); - return MethodHandles.spreadArguments(this, newType); + return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength); } /** @@ -797,15 +797,18 @@ public abstract class MethodHandle { * @see #asVarargsCollector */ public MethodHandle asCollector(Class arrayType, int arrayLength) { + asCollectorChecks(arrayType, arrayLength); + MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength); + return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector); + } + + private void asCollectorChecks(Class arrayType, int arrayLength) { Class arrayElement = arrayType.getComponentType(); - if (arrayElement == null) throw newIllegalArgumentException("not an array type"); - MethodType oldType = type(); - int nargs = oldType.parameterCount(); - if (nargs == 0) throw newIllegalArgumentException("no trailing argument"); - MethodType newType = oldType.dropParameterTypes(nargs-1, nargs); - newType = newType.insertParameterTypes(nargs-1, - java.util.Collections.>nCopies(arrayLength, arrayElement)); - return MethodHandles.collectArguments(this, newType); + if (arrayElement == null) + throw newIllegalArgumentException("not an array type", arrayType); + int nargs = type().parameterCount(); + if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType)) + throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType); } /** diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java index e82442092fe..b17251e4799 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -121,11 +121,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (nargs < INVOKES.length) { MethodHandle invoke = INVOKES[nargs]; MethodType conType = CON_TYPES[nargs]; - MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, null); + MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, 0); if (gcon == null) return null; MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon); assert(galloc.type() == newType.generic()); - return convertArguments(galloc, newType, galloc.type(), null); + return convertArguments(galloc, newType, galloc.type(), 0); } else { MethodHandle invoke = VARARGS_INVOKE; MethodType conType = CON_TYPES[nargs]; @@ -256,8 +256,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; FieldAccessor.ahandle(arrayClass, true) }; if (mhs[0].type().parameterType(0) == Class.class) { - mhs[0] = MethodHandles.insertArguments(mhs[0], 0, elemClass); - mhs[1] = MethodHandles.insertArguments(mhs[1], 0, elemClass); + mhs[0] = mhs[0].bindTo(elemClass); + mhs[1] = mhs[1].bindTo(elemClass); } synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier FieldAccessor.ARRAY_CACHE.put(elemClass, mhs); @@ -372,7 +372,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (evclass != vclass || (!isStatic && ecclass != cclass)) { MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic); strongType = strongType.insertParameterTypes(0, FieldAccessor.class); - mh = MethodHandles.convertArguments(mh, strongType); + mh = convertArguments(mh, strongType, 0); } return mh; } @@ -439,8 +439,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } if (caclass != null) { MethodType strongType = FieldAccessor.atype(caclass, isSetter); - mh = MethodHandles.insertArguments(mh, 0, caclass); - mh = MethodHandles.convertArguments(mh, strongType); + mh = mh.bindTo(caclass); + mh = convertArguments(mh, strongType, 0); } return mh; } @@ -465,7 +465,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) { MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0); MethodType newType = target.type().dropParameterTypes(0, 1); - return convertArguments(bmh, newType, bmh.type(), null); + return convertArguments(bmh, newType, bmh.type(), 0); } } } @@ -486,301 +486,378 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; return new BoundMethodHandle(target, receiver, argnum); } - static MethodHandle convertArguments(MethodHandle target, + static MethodHandle permuteArguments(MethodHandle target, MethodType newType, MethodType oldType, int[] permutationOrNull) { assert(oldType.parameterCount() == target.type().parameterCount()); - if (permutationOrNull != null) { - int outargs = oldType.parameterCount(), inargs = newType.parameterCount(); - if (permutationOrNull.length != outargs) - throw newIllegalArgumentException("wrong number of arguments in permutation"); - // Make the individual outgoing argument types match up first. - Class[] callTypeArgs = new Class[outargs]; - for (int i = 0; i < outargs; i++) - callTypeArgs[i] = newType.parameterType(permutationOrNull[i]); - MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs); - target = convertArguments(target, callType, oldType, null); - assert(target != null); - oldType = target.type(); - List goal = new ArrayList(); // i*TOKEN - List state = new ArrayList(); // i*TOKEN - List drops = new ArrayList(); // not tokens - List dups = new ArrayList(); // not tokens - final int TOKEN = 10; // to mark items which are symbolic only - // state represents the argument values coming into target - for (int i = 0; i < outargs; i++) { - state.add(permutationOrNull[i] * TOKEN); - } - // goal represents the desired state - for (int i = 0; i < inargs; i++) { - if (state.contains(i * TOKEN)) { - goal.add(i * TOKEN); - } else { - // adapter must initially drop all unused arguments - drops.add(i); - } - } - // detect duplications - while (state.size() > goal.size()) { - for (int i2 = 0; i2 < state.size(); i2++) { - int arg1 = state.get(i2); - int i1 = state.indexOf(arg1); - if (i1 != i2) { - // found duplicate occurrence at i2 - int arg2 = (inargs++) * TOKEN; - state.set(i2, arg2); - dups.add(goal.indexOf(arg1)); - goal.add(arg2); - } - } - } - assert(state.size() == goal.size()); - int size = goal.size(); - while (!state.equals(goal)) { - // Look for a maximal sequence of adjacent misplaced arguments, - // and try to rotate them into place. - int bestRotArg = -10 * TOKEN, bestRotLen = 0; - int thisRotArg = -10 * TOKEN, thisRotLen = 0; - for (int i = 0; i < size; i++) { - int arg = state.get(i); - // Does this argument match the current run? - if (arg == thisRotArg + TOKEN) { - thisRotArg = arg; - thisRotLen += 1; - if (bestRotLen < thisRotLen) { - bestRotLen = thisRotLen; - bestRotArg = thisRotArg; - } - } else { - // The old sequence (if any) stops here. - thisRotLen = 0; - thisRotArg = -10 * TOKEN; - // But maybe a new one starts here also. - int wantArg = goal.get(i); - final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION; - if (arg != wantArg && - arg >= wantArg - TOKEN * MAX_ARG_ROTATION && - arg <= wantArg + TOKEN * MAX_ARG_ROTATION) { - thisRotArg = arg; - thisRotLen = 1; - } - } - } - if (bestRotLen >= 2) { - // Do a rotation if it can improve argument positioning - // by at least 2 arguments. This is not always optimal, - // but it seems to catch common cases. - int dstEnd = state.indexOf(bestRotArg); - int srcEnd = goal.indexOf(bestRotArg); - int rotBy = dstEnd - srcEnd; - int dstBeg = dstEnd - (bestRotLen - 1); - int srcBeg = srcEnd - (bestRotLen - 1); - assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs - // Make a span which covers both source and destination. - int rotBeg = Math.min(dstBeg, srcBeg); - int rotEnd = Math.max(dstEnd, srcEnd); - int score = 0; - for (int i = rotBeg; i <= rotEnd; i++) { - if ((int)state.get(i) != (int)goal.get(i)) - score += 1; - } - List rotSpan = state.subList(rotBeg, rotEnd+1); - Collections.rotate(rotSpan, -rotBy); // reverse direction - for (int i = rotBeg; i <= rotEnd; i++) { - if ((int)state.get(i) != (int)goal.get(i)) - score -= 1; - } - if (score >= 2) { - // Improved at least two argument positions. Do it. - List> ptypes = Arrays.asList(oldType.parameterArray()); - Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy); - MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes); - MethodHandle nextTarget - = AdapterMethodHandle.makeRotateArguments(rotType, target, - rotBeg, rotSpan.size(), rotBy); - if (nextTarget != null) { - //System.out.println("Rot: "+rotSpan+" by "+rotBy); - target = nextTarget; - oldType = rotType; - continue; - } - } - // Else de-rotate, and drop through to the swap-fest. - Collections.rotate(rotSpan, rotBy); - } - - // Now swap like the wind! - List> ptypes = Arrays.asList(oldType.parameterArray()); - for (int i = 0; i < size; i++) { - // What argument do I want here? - int arg = goal.get(i); - if (arg != state.get(i)) { - // Where is it now? - int j = state.indexOf(arg); - Collections.swap(ptypes, i, j); - MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes); - target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j); - if (target == null) throw newIllegalArgumentException("cannot swap"); - assert(target.type() == swapType); - oldType = swapType; - Collections.swap(state, i, j); - } - } - // One pass of swapping must finish the job. - assert(state.equals(goal)); - } - while (!dups.isEmpty()) { - // Grab a contiguous trailing sequence of dups. - int grab = dups.size() - 1; - int dupArgPos = dups.get(grab), dupArgCount = 1; - while (grab - 1 >= 0) { - int dup0 = dups.get(grab - 1); - if (dup0 != dupArgPos - 1) break; - dupArgPos -= 1; - dupArgCount += 1; - grab -= 1; - } - //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size())); - dups.subList(grab, dups.size()).clear(); - // In the new target type drop that many args from the tail: - List> ptypes = oldType.parameterList(); - ptypes = ptypes.subList(0, ptypes.size() - dupArgCount); - MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes); - target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount); - if (target == null) - throw newIllegalArgumentException("cannot dup"); - oldType = target.type(); - } - while (!drops.isEmpty()) { - // Grab a contiguous initial sequence of drops. - int dropArgPos = drops.get(0), dropArgCount = 1; - while (dropArgCount < drops.size()) { - int drop1 = drops.get(dropArgCount); - if (drop1 != dropArgPos + dropArgCount) break; - dropArgCount += 1; - } - //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount)); - drops.subList(0, dropArgCount).clear(); - List> dropTypes = newType.parameterList() - .subList(dropArgPos, dropArgPos + dropArgCount); - MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes); - target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount); - if (target == null) throw newIllegalArgumentException("cannot drop"); - oldType = target.type(); + int outargs = oldType.parameterCount(), inargs = newType.parameterCount(); + if (permutationOrNull.length != outargs) + throw newIllegalArgumentException("wrong number of arguments in permutation"); + // Make the individual outgoing argument types match up first. + Class[] callTypeArgs = new Class[outargs]; + for (int i = 0; i < outargs; i++) + callTypeArgs[i] = newType.parameterType(permutationOrNull[i]); + MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs); + target = convertArguments(target, callType, oldType, 0); + assert(target != null); + oldType = target.type(); + List goal = new ArrayList(); // i*TOKEN + List state = new ArrayList(); // i*TOKEN + List drops = new ArrayList(); // not tokens + List dups = new ArrayList(); // not tokens + final int TOKEN = 10; // to mark items which are symbolic only + // state represents the argument values coming into target + for (int i = 0; i < outargs; i++) { + state.add(permutationOrNull[i] * TOKEN); + } + // goal represents the desired state + for (int i = 0; i < inargs; i++) { + if (state.contains(i * TOKEN)) { + goal.add(i * TOKEN); + } else { + // adapter must initially drop all unused arguments + drops.add(i); } } + // detect duplications + while (state.size() > goal.size()) { + for (int i2 = 0; i2 < state.size(); i2++) { + int arg1 = state.get(i2); + int i1 = state.indexOf(arg1); + if (i1 != i2) { + // found duplicate occurrence at i2 + int arg2 = (inargs++) * TOKEN; + state.set(i2, arg2); + dups.add(goal.indexOf(arg1)); + goal.add(arg2); + } + } + } + assert(state.size() == goal.size()); + int size = goal.size(); + while (!state.equals(goal)) { + // Look for a maximal sequence of adjacent misplaced arguments, + // and try to rotate them into place. + int bestRotArg = -10 * TOKEN, bestRotLen = 0; + int thisRotArg = -10 * TOKEN, thisRotLen = 0; + for (int i = 0; i < size; i++) { + int arg = state.get(i); + // Does this argument match the current run? + if (arg == thisRotArg + TOKEN) { + thisRotArg = arg; + thisRotLen += 1; + if (bestRotLen < thisRotLen) { + bestRotLen = thisRotLen; + bestRotArg = thisRotArg; + } + } else { + // The old sequence (if any) stops here. + thisRotLen = 0; + thisRotArg = -10 * TOKEN; + // But maybe a new one starts here also. + int wantArg = goal.get(i); + final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION; + if (arg != wantArg && + arg >= wantArg - TOKEN * MAX_ARG_ROTATION && + arg <= wantArg + TOKEN * MAX_ARG_ROTATION) { + thisRotArg = arg; + thisRotLen = 1; + } + } + } + if (bestRotLen >= 2) { + // Do a rotation if it can improve argument positioning + // by at least 2 arguments. This is not always optimal, + // but it seems to catch common cases. + int dstEnd = state.indexOf(bestRotArg); + int srcEnd = goal.indexOf(bestRotArg); + int rotBy = dstEnd - srcEnd; + int dstBeg = dstEnd - (bestRotLen - 1); + int srcBeg = srcEnd - (bestRotLen - 1); + assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs + // Make a span which covers both source and destination. + int rotBeg = Math.min(dstBeg, srcBeg); + int rotEnd = Math.max(dstEnd, srcEnd); + int score = 0; + for (int i = rotBeg; i <= rotEnd; i++) { + if ((int)state.get(i) != (int)goal.get(i)) + score += 1; + } + List rotSpan = state.subList(rotBeg, rotEnd+1); + Collections.rotate(rotSpan, -rotBy); // reverse direction + for (int i = rotBeg; i <= rotEnd; i++) { + if ((int)state.get(i) != (int)goal.get(i)) + score -= 1; + } + if (score >= 2) { + // Improved at least two argument positions. Do it. + List> ptypes = Arrays.asList(oldType.parameterArray()); + Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy); + MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes); + MethodHandle nextTarget + = AdapterMethodHandle.makeRotateArguments(rotType, target, + rotBeg, rotSpan.size(), rotBy); + if (nextTarget != null) { + //System.out.println("Rot: "+rotSpan+" by "+rotBy); + target = nextTarget; + oldType = rotType; + continue; + } + } + // Else de-rotate, and drop through to the swap-fest. + Collections.rotate(rotSpan, rotBy); + } + + // Now swap like the wind! + List> ptypes = Arrays.asList(oldType.parameterArray()); + for (int i = 0; i < size; i++) { + // What argument do I want here? + int arg = goal.get(i); + if (arg != state.get(i)) { + // Where is it now? + int j = state.indexOf(arg); + Collections.swap(ptypes, i, j); + MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes); + target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j); + if (target == null) throw newIllegalArgumentException("cannot swap"); + assert(target.type() == swapType); + oldType = swapType; + Collections.swap(state, i, j); + } + } + // One pass of swapping must finish the job. + assert(state.equals(goal)); + } + while (!dups.isEmpty()) { + // Grab a contiguous trailing sequence of dups. + int grab = dups.size() - 1; + int dupArgPos = dups.get(grab), dupArgCount = 1; + while (grab - 1 >= 0) { + int dup0 = dups.get(grab - 1); + if (dup0 != dupArgPos - 1) break; + dupArgPos -= 1; + dupArgCount += 1; + grab -= 1; + } + //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size())); + dups.subList(grab, dups.size()).clear(); + // In the new target type drop that many args from the tail: + List> ptypes = oldType.parameterList(); + ptypes = ptypes.subList(0, ptypes.size() - dupArgCount); + MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes); + target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount); + if (target == null) + throw newIllegalArgumentException("cannot dup"); + oldType = target.type(); + } + while (!drops.isEmpty()) { + // Grab a contiguous initial sequence of drops. + int dropArgPos = drops.get(0), dropArgCount = 1; + while (dropArgCount < drops.size()) { + int drop1 = drops.get(dropArgCount); + if (drop1 != dropArgPos + dropArgCount) break; + dropArgCount += 1; + } + //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount)); + drops.subList(0, dropArgCount).clear(); + List> dropTypes = newType.parameterList() + .subList(dropArgPos, dropArgPos + dropArgCount); + MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes); + target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount); + if (target == null) throw newIllegalArgumentException("cannot drop"); + oldType = target.type(); + } + return convertArguments(target, newType, oldType, 0); + } + + /*non-public*/ static + MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) { + MethodType oldType = target.type(); + if (oldType.equals(newType)) + return target; + assert(level > 1 || oldType.isConvertibleTo(newType)); + MethodHandle retFilter = null; + Class oldRT = oldType.returnType(); + Class newRT = newType.returnType(); + if (!VerifyType.isNullConversion(oldRT, newRT)) { + if (oldRT == void.class) { + Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT; + retFilter = ValueConversions.zeroConstantFunction(wrap); + } else { + retFilter = MethodHandles.identity(newRT); + retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level); + } + newType = newType.changeReturnType(oldRT); + } + MethodHandle res = null; + Exception ex = null; + try { + res = convertArguments(target, newType, oldType, level); + } catch (IllegalArgumentException ex1) { + ex = ex1; + } + if (res == null) { + WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target); + wmt.initCause(ex); + throw wmt; + } + if (retFilter != null) + res = MethodHandles.filterReturnValue(res, retFilter); + return res; + } + + static MethodHandle convertArguments(MethodHandle target, + MethodType newType, + MethodType oldType, + int level) { + assert(oldType.parameterCount() == target.type().parameterCount()); if (newType == oldType) return target; if (oldType.parameterCount() != newType.parameterCount()) - throw newIllegalArgumentException("mismatched parameter count"); - MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target); + throw newIllegalArgumentException("mismatched parameter count", oldType, newType); + MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target, level); if (res != null) return res; + // We can come here in the case of target(int)void => (Object)void, + // because the unboxing logic for Object => int is complex. int argc = oldType.parameterCount(); + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated // The JVM can't do it directly, so fill in the gap with a Java adapter. // TO DO: figure out what to put here from case-by-case experience // Use a heavier method: Convert all the arguments to Object, // then back to the desired types. We might have to use Java-based // method handles to do this. MethodType objType = MethodType.genericMethodType(argc); - MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target); + MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target, level); if (objTarget == null) objTarget = FromGeneric.make(target); - res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget); + res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget, level); if (res != null) return res; return ToGeneric.make(newType, objTarget); } + static MethodHandle spreadArguments(MethodHandle target, Class arrayType, int arrayLength) { + MethodType oldType = target.type(); + int nargs = oldType.parameterCount(); + int keepPosArgs = nargs - arrayLength; + MethodType newType = oldType + .dropParameterTypes(keepPosArgs, nargs) + .insertParameterTypes(keepPosArgs, arrayType); + return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength); + } + static MethodHandle spreadArguments(MethodHandle target, MethodType newType, int spreadArgPos) { + int arrayLength = target.type().parameterCount() - spreadArgPos; + return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength); + } static MethodHandle spreadArguments(MethodHandle target, MethodType newType, - int spreadArg) { + int spreadArgPos, + Class arrayType, + int arrayLength) { // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[] MethodType oldType = target.type(); // spread the last argument of newType to oldType - int spreadCount = oldType.parameterCount() - spreadArg; - Class spreadArgType = Object[].class; - MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, spreadArgType, spreadArg, spreadCount); - if (res != null) - return res; - // try an intermediate adapter - Class spreadType = null; - if (spreadArg < 0 || spreadArg >= newType.parameterCount() - || !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg))) - throw newIllegalArgumentException("no restarg in "+newType); - Class[] ptypes = oldType.parameterArray(); - for (int i = 0; i < spreadCount; i++) - ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i); - MethodType midType = MethodType.methodType(newType.returnType(), ptypes); - // after spreading, some arguments may need further conversion - MethodHandle target2 = convertArguments(target, midType, oldType, null); - if (target2 == null) - throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType); - res = AdapterMethodHandle.makeSpreadArguments(newType, target2, spreadArgType, spreadArg, spreadCount); - if (res != null) - return res; - res = SpreadGeneric.make(target2, spreadCount); - if (res != null) - res = convertArguments(res, newType, res.type(), null); + assert(arrayLength == oldType.parameterCount() - spreadArgPos); + assert(newType.parameterType(spreadArgPos) == arrayType); + MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength); + if (res == null) throw new IllegalArgumentException("spread on "+target+" with "+arrayType.getSimpleName()); return res; } + static MethodHandle collectArguments(MethodHandle target, + int collectArg, + MethodHandle collector) { + MethodType type = target.type(); + Class collectType = collector.type().returnType(); + if (collectType != type.parameterType(collectArg)) + target = target.asType(type.changeParameterType(collectArg, collectType)); + MethodType newType = type + .dropParameterTypes(collectArg, collectArg+1) + .insertParameterTypes(collectArg, collector.type().parameterArray()); + return collectArguments(target, newType, collectArg, collector); + } static MethodHandle collectArguments(MethodHandle target, MethodType newType, int collectArg, MethodHandle collector) { MethodType oldType = target.type(); // (a...,c)=>r - if (collector == null) { - int numCollect = newType.parameterCount() - oldType.parameterCount() + 1; - collector = ValueConversions.varargsArray(numCollect); - } // newType // (a..., b...)=>r MethodType colType = collector.type(); // (b...)=>c // oldType // (a..., b...)=>r assert(newType.parameterCount() == collectArg + colType.parameterCount()); assert(oldType.parameterCount() == collectArg + 1); - MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null); - MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, null); - if (gtarget == null || gcollector == null) return null; - MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget); - MethodHandle result = convertArguments(gresult, newType, gresult.type(), null); + MethodHandle result = null; + if (AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false)) { + result = AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false); + } + if (result == null) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0); + MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, 0); + if (gtarget == null || gcollector == null) return null; + MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget); + result = convertArguments(gresult, newType, gresult.type(), 0); + } return result; } static MethodHandle filterArgument(MethodHandle target, - int pos, - MethodHandle filter) { - MethodType ttype = target.type(), gttype = ttype.generic(); + int pos, + MethodHandle filter) { + MethodType ttype = target.type(); + MethodType ftype = filter.type(); + assert(ftype.parameterCount() == 1); + MethodType rtype = ttype.changeParameterType(pos, ftype.parameterType(0)); + MethodType gttype = ttype.generic(); if (ttype != gttype) { - target = convertArguments(target, gttype, ttype, null); + target = convertArguments(target, gttype, ttype, 0); ttype = gttype; } - MethodType ftype = filter.type(), gftype = ftype.generic(); - if (ftype.parameterCount() != 1) - throw new InternalError(); + MethodType gftype = ftype.generic(); if (ftype != gftype) { - filter = convertArguments(filter, gftype, ftype, null); + filter = convertArguments(filter, gftype, ftype, 0); ftype = gftype; } - if (ftype == ttype) { - // simple unary case - return FilterOneArgument.make(filter, target); + MethodHandle result = null; + if (AdapterMethodHandle.canCollectArguments(ttype, ftype, pos, false)) { + result = AdapterMethodHandle.makeCollectArguments(target, filter, pos, false); } - return FilterGeneric.makeArgumentFilter(pos, filter, target); + if (result == null) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + if (ftype == ttype) { + // simple unary case + result = FilterOneArgument.make(filter, target); + } else { + result = FilterGeneric.makeArgumentFilter(pos, filter, target); + } + } + if (result.type() != rtype) + result = result.asType(rtype); + return result; } static MethodHandle foldArguments(MethodHandle target, - MethodType newType, - MethodHandle combiner) { + MethodType newType, + int foldPos, + MethodHandle combiner) { MethodType oldType = target.type(); MethodType ctype = combiner.type(); - MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null); - MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, null); + if (AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true)) { + MethodHandle res = AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true); + if (res != null) return res; + } + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + if (foldPos != 0) return null; + MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0); + MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, 0); + if (ctype.returnType() == void.class) { + gtarget = dropArguments(gtarget, oldType.generic().insertParameterTypes(foldPos, Object.class), foldPos); + } if (gtarget == null || gcombiner == null) return null; MethodHandle gresult = FilterGeneric.makeArgumentFolder(gcombiner, gtarget); - MethodHandle result = convertArguments(gresult, newType, gresult.type(), null); - return result; + return convertArguments(gresult, newType, gresult.type(), 0); } static @@ -802,6 +879,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; this.target = target; this.fallback = fallback; } + // FIXME: Build the control flow out of foldArguments. static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) { MethodType type = target.type(); int nargs = type.parameterCount(); @@ -809,12 +887,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; MethodHandle invoke = INVOKES[nargs]; MethodType gtype = type.generic(); assert(invoke.type().dropParameterTypes(0,1) == gtype); - MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), null); - MethodHandle gtarget = convertArguments(target, gtype, type, null); - MethodHandle gfallback = convertArguments(fallback, gtype, type, null); + MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), 0); + MethodHandle gtarget = convertArguments(target, gtype, type, 0); + MethodHandle gfallback = convertArguments(fallback, gtype, type, 0); if (gtest == null || gtarget == null || gfallback == null) return null; MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback); - return convertArguments(gguard, type, gtype, null); + return convertArguments(gguard, type, gtype, 0); } else { MethodHandle invoke = VARARGS_INVOKE; MethodType gtype = MethodType.genericMethodType(1); @@ -925,8 +1003,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; GuardWithCatch(MethodHandle target, Class exType, MethodHandle catcher) { this(INVOKES[target.type().parameterCount()], target, exType, catcher); } - GuardWithCatch(MethodHandle invoker, - MethodHandle target, Class exType, MethodHandle catcher) { + // FIXME: Build the control flow out of foldArguments. + GuardWithCatch(MethodHandle invoker, + MethodHandle target, Class exType, MethodHandle catcher) { super(invoker); this.target = target; this.exType = exType; @@ -1057,11 +1136,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (nargs < GuardWithCatch.INVOKES.length) { MethodType gtype = type.generic(); MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); - MethodHandle gtarget = convertArguments(target, gtype, type, null); - MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, null); + MethodHandle gtarget = convertArguments(target, gtype, type, 0); + MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 0); MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher); if (gtarget == null || gcatcher == null || gguard == null) return null; - return convertArguments(gguard, type, gtype, null); + return convertArguments(gguard, type, gtype, 0); } else { MethodType gtype = MethodType.genericMethodType(0, true); MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java index 3c7211eb920..40272644e28 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -115,6 +115,8 @@ class MethodHandleNatives { /** Which conv-ops are implemented by the JVM? */ static final int CONV_OP_IMPLEMENTED_MASK; + /** Derived mode flag. Only false on some old JVM implementations. */ + static final boolean HAVE_RICOCHET_FRAMES; private static native void registerNatives(); static { @@ -141,6 +143,7 @@ class MethodHandleNatives { if (CONV_OP_IMPLEMENTED_MASK_ == 0) CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_CONV_OP_IMPLEMENTED_MASK; CONV_OP_IMPLEMENTED_MASK = CONV_OP_IMPLEMENTED_MASK_; + HAVE_RICOCHET_FRAMES = (CONV_OP_IMPLEMENTED_MASK & (1<int, Object->T) OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive - OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI) + OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg) OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg) OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS) OP_DROP_ARGS = 0x9, // remove one or more argument slots - OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI) + OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size) - OP_FLYBY = 0xC, // operate first on reified argument list (NYI) - OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI) + OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result + //OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration /** Shift and mask values for decoding the AMH.conversion field. * These numbers are shared with the JVM for creating AMHs. */ static final int CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field + CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK @@ -244,8 +248,9 @@ class MethodHandleNatives { T_LONG = 11, T_OBJECT = 12, //T_ARRAY = 13 - T_VOID = 14; + T_VOID = 14, //T_ADDRESS = 15 + T_ILLEGAL = 99; /** * Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries. @@ -273,16 +278,29 @@ class MethodHandleNatives { try { Field con = Constants.class.getDeclaredField(name); int jval = con.getInt(null); - if (jval != vmval) - throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval); + if (jval == vmval) continue; + String err = (name+": JVM has "+vmval+" while Java has "+jval); + if (name.equals("CONV_OP_LIMIT")) { + System.err.println("warning: "+err); + continue; + } + throw new InternalError(err); } catch (Exception ex) { + if (ex instanceof NoSuchFieldException) { + String err = (name+": JVM has "+vmval+" which Java does not define"); + // ignore exotic ops the JVM cares about; we just wont issue them + if (name.startsWith("OP_") || name.startsWith("GC_")) { + System.err.println("warning: "+err); + continue; + } + } throw new InternalError(name+": access failed, got "+ex); } } return true; } static { - verifyConstants(); + assert(verifyConstants()); } // Up-calls from the JVM. @@ -323,15 +341,39 @@ class MethodHandleNatives { * The JVM wants to raise an exception. Here's the path. */ static void raiseException(int code, Object actual, Object required) { - String message; - // disregard the identity of the actual object, if it is not a class: - if (!(actual instanceof Class) && !(actual instanceof MethodType)) - actual = actual.getClass(); - if (actual != null) - message = "required "+required+" but encountered "+actual; - else - message = "required "+required; + String message = null; switch (code) { + case 190: // arraylength + try { + String reqLength = ""; + if (required instanceof AdapterMethodHandle) { + int conv = ((AdapterMethodHandle)required).getConversion(); + int spChange = AdapterMethodHandle.extractStackMove(conv); + reqLength = " of length "+(spChange+1); + } + int actualLength = actual == null ? 0 : java.lang.reflect.Array.getLength(actual); + message = "required array"+reqLength+", but encountered wrong length "+actualLength; + break; + } catch (IllegalArgumentException ex) { + } + required = Object[].class; // should have been an array + code = 192; // checkcast + break; + } + // disregard the identity of the actual object, if it is not a class: + if (message == null) { + if (!(actual instanceof Class) && !(actual instanceof MethodType)) + actual = actual.getClass(); + if (actual != null) + message = "required "+required+" but encountered "+actual; + else + message = "required "+required; + } + switch (code) { + case 190: // arraylength + throw new ArrayIndexOutOfBoundsException(message); + case 50: //_aaload + throw new ClassCastException(message); case 192: // checkcast throw new ClassCastException(message); default: @@ -365,4 +407,13 @@ class MethodHandleNatives { throw err; } } + + /** + * This assertion marks code which was written before ricochet frames were implemented. + * Such code will go away when the ports catch up. + */ + static boolean workaroundWithoutRicochetFrames() { + assert(!HAVE_RICOCHET_FRAMES) : "this code should not be executed if `-XX:+UseRicochetFrames is enabled"; + return true; + } } diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java index 6ea8f978b63..3eeeec71ac3 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java @@ -63,8 +63,17 @@ package java.lang.invoke; } static void checkSpreadArgument(Object av, int n) { - if (av == null ? n != 0 : ((Object[])av).length != n) - throw newIllegalArgumentException("Array is not of length "+n); + if (av == null) { + if (n == 0) return; + } else if (av instanceof Object[]) { + int len = ((Object[])av).length; + if (len == n) return; + } else { + int len = java.lang.reflect.Array.getLength(av); + if (len == n) return; + } + // fall through to error: + throw newIllegalArgumentException("Array is not of length "+n); } // handy shared exception makers (they simplify the common case code) @@ -80,6 +89,9 @@ package java.lang.invoke; /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) { return new IllegalArgumentException(message(message, obj)); } + /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) { + return new IllegalArgumentException(message(message, obj, obj2)); + } /*non-public*/ static Error uncaughtException(Exception ex) { Error err = new InternalError("uncaught exception"); err.initCause(ex); @@ -89,4 +101,8 @@ package java.lang.invoke; if (obj != null) message = message + ": " + obj; return message; } + private static String message(String message, Object obj, Object obj2) { + if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2; + return message; + } } diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java index 40af515771a..99985a09fee 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java @@ -1080,7 +1080,7 @@ return mh1; MethodType rawType = mh.type(); if (rawType.parameterType(0) == caller) return mh; MethodType narrowType = rawType.changeParameterType(0, caller); - MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, null); + MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0); return fixVarargs(narrowMH, mh); } @@ -1377,18 +1377,7 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type) */ public static MethodHandle convertArguments(MethodHandle target, MethodType newType) { - MethodType oldType = target.type(); - if (oldType.equals(newType)) - return target; - MethodHandle res = null; - try { - res = MethodHandleImpl.convertArguments(target, - newType, oldType, null); - } catch (IllegalArgumentException ex) { - } - if (res == null) - throw new WrongMethodTypeException("cannot convert to "+newType+": "+target); - return res; + return MethodHandleImpl.convertArguments(target, newType, 1); } /** @@ -1431,7 +1420,7 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type) */ public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { - return convertArguments(target, newType); // FIXME! + return MethodHandleImpl.convertArguments(target, newType, 2); } /* @@ -1526,23 +1515,32 @@ assert((int)twice.invokeExact(21) == 42); MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { MethodType oldType = target.type(); checkReorder(reorder, newType, oldType); - return MethodHandleImpl.convertArguments(target, + return MethodHandleImpl.permuteArguments(target, newType, oldType, reorder); } private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) { + if (newType.returnType() != oldType.returnType()) + throw newIllegalArgumentException("return types do not match", + oldType, newType); if (reorder.length == oldType.parameterCount()) { int limit = newType.parameterCount(); boolean bad = false; - for (int i : reorder) { + for (int j = 0; j < reorder.length; j++) { + int i = reorder[j]; if (i < 0 || i >= limit) { bad = true; break; } + Class src = newType.parameterType(i); + Class dst = oldType.parameterType(j); + if (src != dst) + throw newIllegalArgumentException("parameter types do not match after reorder", + oldType, newType); } if (!bad) return; } - throw newIllegalArgumentException("bad reorder array"); + throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder)); } /** @@ -1631,7 +1629,7 @@ assert((int)twice.invokeExact(21) == 42); if (type == void.class) throw newIllegalArgumentException("void type"); Wrapper w = Wrapper.forPrimitiveType(type); - return identity(type).bindTo(w.convert(value, type)); + return insertArguments(identity(type), 0, w.convert(value, type)); } else { return identity(type).bindTo(type.cast(value)); } @@ -1866,7 +1864,8 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { MethodType targetType = target.type(); MethodHandle adapter = target; - MethodType adapterType = targetType; + MethodType adapterType = null; + assert((adapterType = targetType) != null); int maxPos = targetType.parameterCount(); if (pos + filters.length > maxPos) throw newIllegalArgumentException("too many filters"); @@ -1874,19 +1873,23 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY for (MethodHandle filter : filters) { curPos += 1; if (filter == null) continue; // ignore null elements of filters - MethodType filterType = filter.type(); - if (filterType.parameterCount() != 1 - || filterType.returnType() != targetType.parameterType(curPos)) - throw newIllegalArgumentException("target and filter types do not match"); - adapterType = adapterType.changeParameterType(curPos, filterType.parameterType(0)); - adapter = MethodHandleImpl.filterArgument(adapter, curPos, filter); + adapter = filterArgument(adapter, curPos, filter); + assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null); } - MethodType midType = adapter.type(); - if (midType != adapterType) - adapter = MethodHandleImpl.convertArguments(adapter, adapterType, midType, null); + assert(adapterType.equals(adapter.type())); return adapter; } + /*non-public*/ static + MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) { + MethodType targetType = target.type(); + MethodType filterType = filter.type(); + if (filterType.parameterCount() != 1 + || filterType.returnType() != targetType.parameterType(pos)) + throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); + return MethodHandleImpl.filterArgument(target, pos, filter); + } + /** * Adapts a target method handle {@code target} by post-processing * its return value with a unary filter function. @@ -1922,14 +1925,26 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) { MethodType targetType = target.type(); MethodType filterType = filter.type(); - if (filterType.parameterCount() != 1 - || filterType.parameterType(0) != targetType.returnType()) - throw newIllegalArgumentException("target and filter types do not match"); + Class rtype = targetType.returnType(); + int filterValues = filterType.parameterCount(); + if (filterValues == 0 + ? (rtype != void.class) + : (rtype != filterType.parameterType(0))) + throw newIllegalArgumentException("target and filter types do not match", target, filter); // result = fold( lambda(retval, arg...) { filter(retval) }, // lambda( arg...) { target(arg...) } ) + MethodType newType = targetType.changeReturnType(filterType.returnType()); + MethodHandle result = null; + if (AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false)) { + result = AdapterMethodHandle.makeCollectArguments(filter, target, 0, false); + if (result != null) return result; + } // FIXME: Too many nodes here. - MethodHandle returner = dropArguments(filter, 1, targetType.parameterList()); - return foldArguments(returner, target); + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + MethodHandle returner = dropArguments(filter, filterValues, targetType.parameterList()); + result = foldArguments(returner, target); + assert(result.type().equals(newType)); + return result; } /** @@ -1981,16 +1996,23 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { MethodType targetType = target.type(); MethodType combinerType = combiner.type(); + int foldPos = 0; // always at the head, at present int foldArgs = combinerType.parameterCount(); - boolean ok = (targetType.parameterCount() >= 1 + foldArgs); - if (ok && !combinerType.parameterList().equals(targetType.parameterList().subList(1, foldArgs+1))) + int foldVals = combinerType.returnType() == void.class ? 0 : 1; + int afterInsertPos = foldPos + foldVals; + boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs); + if (ok && !(combinerType.parameterList() + .equals(targetType.parameterList().subList(afterInsertPos, + afterInsertPos + foldArgs)))) ok = false; - if (ok && !combinerType.returnType().equals(targetType.parameterType(0))) + if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0))) ok = false; if (!ok) throw misMatchedTypes("target and combiner types", targetType, combinerType); - MethodType newType = targetType.dropParameterTypes(0, 1); - return MethodHandleImpl.foldArguments(target, newType, combiner); + MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos); + MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner); + if (res == null) throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType); + return res; } /** diff --git a/jdk/src/share/classes/java/lang/invoke/MethodType.java b/jdk/src/share/classes/java/lang/invoke/MethodType.java index ff3d213a341..02c12c69aad 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodType.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java @@ -25,6 +25,7 @@ package java.lang.invoke; +import sun.invoke.util.Wrapper; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -262,18 +263,18 @@ class MethodType implements java.io.Serializable { * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array. * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * All parameters and the return type will be {@code Object}, - * except the final varargs parameter if any, which will be {@code Object[]}. - * @param objectArgCount number of parameters (excluding the varargs parameter if any) - * @param varargs whether there will be a varargs parameter, of type {@code Object[]} - * @return a totally generic method type, given only its count of parameters and varargs - * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 + * except the final array parameter if any, which will be {@code Object[]}. + * @param objectArgCount number of parameters (excluding the final array parameter if any) + * @param finalArray whether there will be a trailing array parameter, of type {@code Object[]} + * @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments + * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray}) * @see #genericMethodType(int) */ public static - MethodType genericMethodType(int objectArgCount, boolean varargs) { + MethodType genericMethodType(int objectArgCount, boolean finalArray) { MethodType mt; checkSlotCount(objectArgCount); - int ivarargs = (!varargs ? 0 : 1); + int ivarargs = (!finalArray ? 0 : 1); int ootIndex = objectArgCount*2 + ivarargs; if (ootIndex < objectOnlyTypes.length) { mt = objectOnlyTypes[ootIndex]; @@ -626,6 +627,30 @@ class MethodType implements java.io.Serializable { return sb.toString(); } + + /*non-public*/ + boolean isConvertibleTo(MethodType newType) { + if (!canConvert(returnType(), newType.returnType())) + return false; + int argc = parameterCount(); + if (argc != newType.parameterCount()) + return false; + for (int i = 0; i < argc; i++) { + if (!canConvert(newType.parameterType(i), parameterType(i))) + return false; + } + return true; + } + private static boolean canConvert(Class src, Class dst) { + if (src == dst || dst == void.class) return true; + if (src.isPrimitive() && dst.isPrimitive()) { + if (!Wrapper.forPrimitiveType(dst) + .isConvertibleFrom(Wrapper.forPrimitiveType(src))) + return false; + } + return true; + } + /// Queries which have to do with the bytecode architecture /** Reports the number of JVM stack slots required to invoke a method diff --git a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java index 92a9031e0cb..6ad4a5a8eea 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java @@ -46,6 +46,7 @@ class MethodTypeForm { final long argCounts; // packed slot & value counts final long primCounts; // packed prim & double counts final int vmslots; // total number of parameter slots + private Object vmlayout; // vm-specific information for calls final MethodType erasedType; // the canonical erasure /*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers diff --git a/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java b/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java index a862723e025..be160667381 100644 --- a/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java +++ b/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java @@ -66,6 +66,10 @@ class SpreadGeneric { this.entryPoint = ep[0]; } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + /** From targetType remove the last spreadCount arguments, and instead * append a simple Object argument. */ diff --git a/jdk/src/share/classes/java/lang/invoke/ToGeneric.java b/jdk/src/share/classes/java/lang/invoke/ToGeneric.java index 22723975d5b..2d46a3910e1 100644 --- a/jdk/src/share/classes/java/lang/invoke/ToGeneric.java +++ b/jdk/src/share/classes/java/lang/invoke/ToGeneric.java @@ -96,7 +96,7 @@ class ToGeneric { ToGeneric va2 = ToGeneric.of(primsAtEnd); this.adapter = va2.adapter; if (true) throw new UnsupportedOperationException("NYI: primitive parameters must follow references; entryType = "+entryType); - this.entryPoint = MethodHandleImpl.convertArguments( + this.entryPoint = MethodHandleImpl.permuteArguments( va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder); // example: for entryType of (int,Object,Object), the reordered // type is (Object,Object,int) and the order is {1,2,0}, @@ -128,7 +128,7 @@ class ToGeneric { assert(eptWithInts.parameterType(i) == int.class); MethodType nextType = midType.changeParameterType(i, int.class); rawEntryPoint = MethodHandleImpl.convertArguments( - rawEntryPoint, nextType, midType, null); + rawEntryPoint, nextType, midType, 0); midType = nextType; } } @@ -152,6 +152,10 @@ class ToGeneric { this.invoker = makeRawArgumentFilter(invoker0, rawEntryTypeInit, entryType); } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + /** A generic argument list will be created by a call of type 'raw'. * The values need to be reboxed for to match 'cooked'. * Do this on the fly. @@ -171,7 +175,7 @@ class ToGeneric { invoker.type().generic(), invoker, 0, MethodHandle.class); if (filteredInvoker == null) throw new UnsupportedOperationException("NYI"); } - MethodHandle reboxer = ValueConversions.rebox(dst, false); + MethodHandle reboxer = ValueConversions.rebox(dst); filteredInvoker = FilterGeneric.makeArgumentFilter(1+i, reboxer, filteredInvoker); if (filteredInvoker == null) throw new InternalError(); } @@ -199,13 +203,13 @@ class ToGeneric { assert(!rret.isPrimitive()); if (rret == Object.class && !mustCast) return null; - return ValueConversions.cast(tret, false); + return ValueConversions.cast(tret); } else if (tret == rret) { - return ValueConversions.unbox(tret, false); + return ValueConversions.unbox(tret); } else { assert(rret.isPrimitive()); assert(tret == double.class ? rret == long.class : rret == int.class); - return ValueConversions.unboxRaw(tret, false); + return ValueConversions.unboxRaw(tret); } } @@ -311,7 +315,7 @@ class ToGeneric { } static Adapter buildAdapterFromBytecodes(MethodType entryPointType) { - throw new UnsupportedOperationException("NYI"); + throw new UnsupportedOperationException("NYI: "+entryPointType); } /** diff --git a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java index c6a56abf665..06e9a278daa 100644 --- a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java +++ b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java @@ -31,10 +31,15 @@ import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumMap; import java.util.List; public class ValueConversions { + private static final Class THIS_CLASS = ValueConversions.class; + // Do not adjust this except for special platforms: + private static final int MAX_ARITY = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255); + private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); private static EnumMap[] newWrapperCaches(int n) { @@ -42,88 +47,101 @@ public class ValueConversions { EnumMap[] caches = (EnumMap[]) new EnumMap[n]; // unchecked warning expected here for (int i = 0; i < n; i++) - caches[i] = new EnumMap(Wrapper.class); + caches[i] = new EnumMap<>(Wrapper.class); return caches; } /// Converting references to values. - static int unboxInteger(Object x) { - if (x == null) return 0; // never NPE - return ((Integer) x).intValue(); + // There are several levels of this unboxing conversions: + // no conversions: exactly Integer.valueOf, etc. + // implicit conversions sanctioned by JLS 5.1.2, etc. + // explicit conversions as allowed by explicitCastArguments + + static int unboxInteger(Object x, boolean cast) { + if (x instanceof Integer) + return ((Integer) x).intValue(); + return primitiveConversion(Wrapper.INT, x, cast).intValue(); } - static byte unboxByte(Object x) { - if (x == null) return 0; // never NPE - return ((Byte) x).byteValue(); + static byte unboxByte(Object x, boolean cast) { + if (x instanceof Byte) + return ((Byte) x).byteValue(); + return primitiveConversion(Wrapper.BYTE, x, cast).byteValue(); } - static short unboxShort(Object x) { - if (x == null) return 0; // never NPE - return ((Short) x).shortValue(); + static short unboxShort(Object x, boolean cast) { + if (x instanceof Short) + return ((Short) x).shortValue(); + return primitiveConversion(Wrapper.SHORT, x, cast).shortValue(); } - static boolean unboxBoolean(Object x) { - if (x == null) return false; // never NPE - return ((Boolean) x).booleanValue(); + static boolean unboxBoolean(Object x, boolean cast) { + if (x instanceof Boolean) + return ((Boolean) x).booleanValue(); + return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0; } - static char unboxCharacter(Object x) { - if (x == null) return 0; // never NPE - return ((Character) x).charValue(); + static char unboxCharacter(Object x, boolean cast) { + if (x instanceof Character) + return ((Character) x).charValue(); + return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue(); } - static long unboxLong(Object x) { - if (x == null) return 0; // never NPE - return ((Long) x).longValue(); + static long unboxLong(Object x, boolean cast) { + if (x instanceof Long) + return ((Long) x).longValue(); + return primitiveConversion(Wrapper.LONG, x, cast).longValue(); } - static float unboxFloat(Object x) { - if (x == null) return 0; // never NPE - return ((Float) x).floatValue(); + static float unboxFloat(Object x, boolean cast) { + if (x instanceof Float) + return ((Float) x).floatValue(); + return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue(); } - static double unboxDouble(Object x) { - if (x == null) return 0; // never NPE - return ((Double) x).doubleValue(); + static double unboxDouble(Object x, boolean cast) { + if (x instanceof Double) + return ((Double) x).doubleValue(); + return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue(); } /// Converting references to "raw" values. /// A raw primitive value is always an int or long. - static int unboxByteRaw(Object x) { - return unboxByte(x); + static int unboxByteRaw(Object x, boolean cast) { + return unboxByte(x, cast); } - static int unboxShortRaw(Object x) { - return unboxShort(x); + static int unboxShortRaw(Object x, boolean cast) { + return unboxShort(x, cast); } - static int unboxBooleanRaw(Object x) { - return unboxBoolean(x) ? 1 : 0; + static int unboxBooleanRaw(Object x, boolean cast) { + return unboxBoolean(x, cast) ? 1 : 0; } - static int unboxCharacterRaw(Object x) { - return unboxCharacter(x); + static int unboxCharacterRaw(Object x, boolean cast) { + return unboxCharacter(x, cast); } - static int unboxFloatRaw(Object x) { - return Float.floatToIntBits(unboxFloat(x)); + static int unboxFloatRaw(Object x, boolean cast) { + return Float.floatToIntBits(unboxFloat(x, cast)); } - static long unboxDoubleRaw(Object x) { - return Double.doubleToRawLongBits(unboxDouble(x)); + static long unboxDoubleRaw(Object x, boolean cast) { + return Double.doubleToRawLongBits(unboxDouble(x, cast)); } private static MethodType unboxType(Wrapper wrap, boolean raw) { - return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType()); + return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), Object.class, boolean.class); } private static final EnumMap[] UNBOX_CONVERSIONS = newWrapperCaches(4); - private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) { - EnumMap cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)]; + private static MethodHandle unbox(Wrapper wrap, boolean raw, boolean cast) { + EnumMap cache = UNBOX_CONVERSIONS[(cast?1:0)+(raw?2:0)]; MethodHandle mh = cache.get(wrap); if (mh != null) { return mh; @@ -136,7 +154,7 @@ public class ValueConversions { mh = raw ? ALWAYS_ZERO : IGNORE; break; case INT: case LONG: // these guys don't need separate raw channels - if (raw) mh = unbox(wrap, exact, false); + if (raw) mh = unbox(wrap, false, cast); break; } if (mh != null) { @@ -146,37 +164,62 @@ public class ValueConversions { // look up the method String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : ""); MethodType type = unboxType(wrap, raw); - if (!exact) { - try { - // actually, type is wrong; the Java method takes Object - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase()); - } catch (ReflectiveOperationException ex) { - mh = null; - } - } else { - mh = unbox(wrap, !exact, raw).asType(type); + try { + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); + } catch (ReflectiveOperationException ex) { + mh = null; } if (mh != null) { + mh = MethodHandles.insertArguments(mh, 1, cast); cache.put(wrap, mh); return mh; } - throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : "")); + throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + + (cast ? " (cast)" : "") + (raw ? " (raw)" : "")); } - public static MethodHandle unbox(Wrapper type, boolean exact) { - return unbox(type, exact, false); + public static MethodHandle unboxCast(Wrapper type) { + return unbox(type, false, true); } - public static MethodHandle unboxRaw(Wrapper type, boolean exact) { - return unbox(type, exact, true); + public static MethodHandle unboxRaw(Wrapper type) { + return unbox(type, true, false); } - public static MethodHandle unbox(Class type, boolean exact) { - return unbox(Wrapper.forPrimitiveType(type), exact, false); + public static MethodHandle unbox(Class type) { + return unbox(Wrapper.forPrimitiveType(type), false, false); } - public static MethodHandle unboxRaw(Class type, boolean exact) { - return unbox(Wrapper.forPrimitiveType(type), exact, true); + public static MethodHandle unboxCast(Class type) { + return unbox(Wrapper.forPrimitiveType(type), false, true); + } + + public static MethodHandle unboxRaw(Class type) { + return unbox(Wrapper.forPrimitiveType(type), true, false); + } + + /// Primitive conversions + public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) { + // Maybe merge this code with Wrapper.convert/cast. + Number res = null; + if (x == null) { + if (!cast) return null; + x = wrap.zero(); + } + if (x instanceof Number) { + res = (Number) x; + } else if (x instanceof Boolean) { + res = ((boolean)x ? 1 : 0); + } else if (x instanceof Character) { + res = (int)(char)x; + } else { + // this will fail with the required ClassCastException: + res = (Number) x; + } + if (!cast && !wrap.isConvertibleFrom(Wrapper.forWrapperType(x.getClass()))) + // this will fail with the required ClassCastException: + res = (Number) wrap.wrapperType().cast(x); + return res; } /// Converting primitives to references @@ -285,7 +328,7 @@ public class ValueConversions { MethodType type = boxType(wrap, raw); if (exact) { try { - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); } catch (ReflectiveOperationException ex) { mh = null; } @@ -296,22 +339,31 @@ public class ValueConversions { cache.put(wrap, mh); return mh; } - throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : "")); + throw new IllegalArgumentException("cannot find box adapter for " + + wrap + (exact ? " (exact)" : "") + (raw ? " (raw)" : "")); } - public static MethodHandle box(Class type, boolean exact) { + public static MethodHandle box(Class type) { + boolean exact = false; + // e.g., boxShort(short)Short if exact, + // e.g., boxShort(short)Object if !exact return box(Wrapper.forPrimitiveType(type), exact, false); } - public static MethodHandle boxRaw(Class type, boolean exact) { + public static MethodHandle boxRaw(Class type) { + boolean exact = false; + // e.g., boxShortRaw(int)Short if exact + // e.g., boxShortRaw(int)Object if !exact return box(Wrapper.forPrimitiveType(type), exact, true); } - public static MethodHandle box(Wrapper type, boolean exact) { + public static MethodHandle box(Wrapper type) { + boolean exact = false; return box(type, exact, false); } - public static MethodHandle boxRaw(Wrapper type, boolean exact) { + public static MethodHandle boxRaw(Wrapper type) { + boolean exact = false; return box(type, exact, true); } @@ -319,16 +371,16 @@ public class ValueConversions { static int unboxRawInteger(Object x) { if (x instanceof Integer) - return unboxInteger(x); + return (int) x; else - return (int) unboxLong(x); + return (int) unboxLong(x, false); } static Integer reboxRawInteger(Object x) { if (x instanceof Integer) return (Integer) x; else - return (int) unboxLong(x); + return (int) unboxLong(x, false); } static Byte reboxRawByte(Object x) { @@ -362,7 +414,7 @@ public class ValueConversions { static Double reboxRawDouble(Object x) { if (x instanceof Double) return (Double) x; - return boxDoubleRaw(unboxLong(x)); + return boxDoubleRaw(unboxLong(x, true)); } private static MethodType reboxType(Wrapper wrap) { @@ -371,7 +423,7 @@ public class ValueConversions { } private static final EnumMap[] - REBOX_CONVERSIONS = newWrapperCaches(2); + REBOX_CONVERSIONS = newWrapperCaches(1); /** * Because we normalize primitive types to reduce the number of signatures, @@ -380,10 +432,10 @@ public class ValueConversions { * When the erased primitive value is then boxed into an Integer or Long, * the final boxed primitive is sometimes required. This transformation * is called a "rebox". It takes an Integer or Long and produces some - * other boxed value. + * other boxed value, typed (inexactly) as an Object */ - public static MethodHandle rebox(Wrapper wrap, boolean exact) { - EnumMap cache = REBOX_CONVERSIONS[exact?1:0]; + public static MethodHandle rebox(Wrapper wrap) { + EnumMap cache = REBOX_CONVERSIONS[0]; MethodHandle mh = cache.get(wrap); if (mh != null) { return mh; @@ -402,14 +454,11 @@ public class ValueConversions { // look up the method String name = "reboxRaw" + wrap.simpleName(); MethodType type = reboxType(wrap); - if (exact) { - try { - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - } else { - mh = rebox(wrap, !exact).asType(IDENTITY.type()); + try { + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); + mh = mh.asType(IDENTITY.type()); + } catch (ReflectiveOperationException ex) { + mh = null; } if (mh != null) { cache.put(wrap, mh); @@ -418,8 +467,8 @@ public class ValueConversions { throw new IllegalArgumentException("cannot find rebox adapter for " + wrap); } - public static MethodHandle rebox(Class type, boolean exact) { - return rebox(Wrapper.forPrimitiveType(type), exact); + public static MethodHandle rebox(Class type) { + return rebox(Wrapper.forPrimitiveType(type)); } /// Width-changing conversions between int and long. @@ -486,9 +535,10 @@ public class ValueConversions { case VOID: mh = EMPTY; break; + case OBJECT: case INT: case LONG: case FLOAT: case DOUBLE: try { - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type); + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.simpleName(), type); } catch (ReflectiveOperationException ex) { mh = null; } @@ -592,7 +642,7 @@ public class ValueConversions { return t.cast(x); } - private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY; + private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY; static { try { MethodType idType = MethodType.genericMethodType(1); @@ -600,40 +650,56 @@ public class ValueConversions { MethodType alwaysZeroType = idType.changeReturnType(int.class); MethodType ignoreType = idType.changeReturnType(void.class); MethodType zeroObjectType = MethodType.genericMethodType(0); - IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType); - IDENTITY_I = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(int.class, int.class)); - IDENTITY_J = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(long.class, long.class)); + IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType); + IDENTITY_I = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(int.class, int.class)); + IDENTITY_J = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(long.class, long.class)); //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType); - CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType); - ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType); - ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType); - ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType); - IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType); - EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1)); - } catch (Exception ex) { + CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType); + ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType); + ALWAYS_ZERO = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysZero", alwaysZeroType); + ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType); + IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType); + EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1)); + NEW_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "newArray", MethodType.methodType(Object[].class, int.class)); + } catch (NoSuchMethodException | IllegalAccessException ex) { Error err = new InternalError("uncaught exception"); err.initCause(ex); throw err; } } - private static final EnumMap WRAPPER_CASTS - = new EnumMap(Wrapper.class); + // Varargs methods need to be in a separately initialized class, to bootstrapping problems. + static class LazyStatics { + private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST; + static { + try { + //MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class)); + COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class)); + COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class)); + MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class)); + } catch (ReflectiveOperationException ex) { + Error err = new InternalError("uncaught exception"); + err.initCause(ex); + throw err; + } + } + } - private static final EnumMap EXACT_WRAPPER_CASTS - = new EnumMap(Wrapper.class); + private static final EnumMap[] WRAPPER_CASTS + = newWrapperCaches(2); /** Return a method that casts its sole argument (an Object) to the given type * and returns it as the given type (if exact is true), or as plain Object (if erase is true). */ - public static MethodHandle cast(Class type, boolean exact) { + public static MethodHandle cast(Class type) { + boolean exact = false; if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type); MethodHandle mh = null; Wrapper wrap = null; EnumMap cache = null; if (Wrapper.isWrapperType(type)) { wrap = Wrapper.forWrapperType(type); - cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS); + cache = WRAPPER_CASTS[exact?1:0]; mh = cache.get(wrap); if (mh != null) return mh; } @@ -673,7 +739,7 @@ public class ValueConversions { if (wrap != Wrapper.VOID) type = type.appendParameterTypes(wrap.primitiveType()); try { - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type); + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type); } catch (ReflectiveOperationException ex) { mh = null; } @@ -692,6 +758,210 @@ public class ValueConversions { throw new IllegalArgumentException("cannot find identity for " + wrap); } + /// Float/non-float conversions. + + static float doubleToFloat(double x) { + return (float) x; + } + static double floatToDouble(float x) { + return x; + } + + // narrow double to integral type + static long doubleToLong(double x) { + return (long) x; + } + static int doubleToInt(double x) { + return (int) x; + } + static short doubleToShort(double x) { + return (short) x; + } + static char doubleToChar(double x) { + return (char) x; + } + static byte doubleToByte(double x) { + return (byte) x; + } + static boolean doubleToBoolean(double x) { + return toBoolean((byte) x); + } + + // narrow float to integral type + static long floatToLong(float x) { + return (long) x; + } + static int floatToInt(float x) { + return (int) x; + } + static short floatToShort(float x) { + return (short) x; + } + static char floatToChar(float x) { + return (char) x; + } + static byte floatToByte(float x) { + return (byte) x; + } + static boolean floatToBoolean(float x) { + return toBoolean((byte) x); + } + + // widen integral type to double + static double longToDouble(long x) { + return x; + } + static double intToDouble(int x) { + return x; + } + static double shortToDouble(short x) { + return x; + } + static double charToDouble(char x) { + return x; + } + static double byteToDouble(byte x) { + return x; + } + static double booleanToDouble(boolean x) { + return fromBoolean(x); + } + + // widen integral type to float + static float longToFloat(long x) { + return x; + } + static float intToFloat(int x) { + return x; + } + static float shortToFloat(short x) { + return x; + } + static float charToFloat(char x) { + return x; + } + static float byteToFloat(byte x) { + return x; + } + static float booleanToFloat(boolean x) { + return fromBoolean(x); + } + + static boolean toBoolean(byte x) { + // see javadoc for MethodHandles.explicitCastArguments + return ((x & 1) != 0); + } + static byte fromBoolean(boolean x) { + // see javadoc for MethodHandles.explicitCastArguments + return (x ? (byte)1 : (byte)0); + } + + private static final EnumMap[] + CONVERT_FLOAT_FUNCTIONS = newWrapperCaches(4); + + static MethodHandle convertFloatFunction(Wrapper wrap, boolean toFloat, boolean doubleSize) { + EnumMap cache = CONVERT_FLOAT_FUNCTIONS[(toFloat?1:0)+(doubleSize?2:0)]; + MethodHandle mh = cache.get(wrap); + if (mh != null) { + return mh; + } + // slow path + Wrapper fwrap = (doubleSize ? Wrapper.DOUBLE : Wrapper.FLOAT); + Class fix = wrap.primitiveType(); + Class flt = (doubleSize ? double.class : float.class); + Class src = toFloat ? fix : flt; + Class dst = toFloat ? flt : fix; + if (src == dst) return identity(wrap); + MethodType type = MethodType.methodType(dst, src); + switch (wrap) { + case VOID: + mh = toFloat ? zeroConstantFunction(fwrap) : MethodHandles.dropArguments(EMPTY, 0, flt); + break; + case OBJECT: + mh = toFloat ? unbox(flt) : box(flt); + break; + default: + try { + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type); + } catch (ReflectiveOperationException ex) { + mh = null; + } + break; + } + if (mh != null) { + assert(mh.type() == type) : mh; + cache.put(wrap, mh); + return mh; + } + + throw new IllegalArgumentException("cannot find float conversion constant for " + + src.getSimpleName()+" -> "+dst.getSimpleName()); + } + + public static MethodHandle convertFromFloat(Class fixType) { + Wrapper wrap = Wrapper.forPrimitiveType(fixType); + return convertFloatFunction(wrap, false, false); + } + public static MethodHandle convertFromDouble(Class fixType) { + Wrapper wrap = Wrapper.forPrimitiveType(fixType); + return convertFloatFunction(wrap, false, true); + } + public static MethodHandle convertToFloat(Class fixType) { + Wrapper wrap = Wrapper.forPrimitiveType(fixType); + return convertFloatFunction(wrap, true, false); + } + public static MethodHandle convertToDouble(Class fixType) { + Wrapper wrap = Wrapper.forPrimitiveType(fixType); + return convertFloatFunction(wrap, true, true); + } + + private static String capitalize(String x) { + return Character.toUpperCase(x.charAt(0))+x.substring(1); + } + + /// Collection of multiple arguments. + + public static Object convertArrayElements(Class arrayType, Object array) { + Class src = array.getClass().getComponentType(); + Class dst = arrayType.getComponentType(); + if (src == null || dst == null) throw new IllegalArgumentException("not array type"); + Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null); + Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null); + int length; + if (sw == null) { + Object[] a = (Object[]) array; + length = a.length; + if (dw == null) + return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class)); + Object res = dw.makeArray(length); + dw.copyArrayUnboxing(a, 0, res, 0, length); + return res; + } + length = java.lang.reflect.Array.getLength(array); + Object[] res; + if (dw == null) { + res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class)); + } else { + res = new Object[length]; + } + sw.copyArrayBoxing(array, 0, res, 0, length); + if (dw == null) return res; + Object a = dw.makeArray(length); + dw.copyArrayUnboxing(res, 0, a, 0, length); + return a; + } + + private static MethodHandle findCollector(String name, int nargs, Class rtype, Class... ptypes) { + MethodType type = MethodType.genericMethodType(nargs) + .changeReturnType(rtype) + .insertParameterTypes(0, ptypes); + try { + return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); + } catch (ReflectiveOperationException ex) { + return null; + } + } + private static final Object[] NO_ARGS_ARRAY = {}; private static Object[] makeArray(Object... args) { return args; } private static Object[] array() { return NO_ARGS_ARRAY; } @@ -723,36 +993,176 @@ public class ValueConversions { Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } - static MethodHandle[] makeArrays() { - ArrayList arrays = new ArrayList(); - MethodHandles.Lookup lookup = IMPL_LOOKUP; + private static MethodHandle[] makeArrays() { + ArrayList mhs = new ArrayList<>(); for (;;) { - int nargs = arrays.size(); - MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class); - String name = "array"; - MethodHandle array = null; - try { - array = lookup.findStatic(ValueConversions.class, name, type); - } catch (ReflectiveOperationException ex) { - } - if (array == null) break; - arrays.add(array); + MethodHandle mh = findCollector("array", mhs.size(), Object[].class); + if (mh == null) break; + mhs.add(mh); } - assert(arrays.size() == 11); // current number of methods - return arrays.toArray(new MethodHandle[0]); + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[MAX_ARITY+1]); + } + private static final MethodHandle[] ARRAYS = makeArrays(); + + // mh-fill versions of the above: + private static Object[] newArray(int len) { return new Object[len]; } + private static void fillWithArguments(Object[] a, int pos, Object... args) { + System.arraycopy(args, 0, a, pos, args.length); + } + // using Integer pos instead of int pos to avoid bootstrapping problems + private static Object[] fillArray(Object[] a, Integer pos, Object a0) + { fillWithArguments(a, pos, a0); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1) + { fillWithArguments(a, pos, a0, a1); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2) + { fillWithArguments(a, pos, a0, a1, a2); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3) + { fillWithArguments(a, pos, a0, a1, a2, a3); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } + private static MethodHandle[] makeFillArrays() { + ArrayList mhs = new ArrayList<>(); + mhs.add(null); // there is no empty fill; at least a0 is required + for (;;) { + MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Object[].class, Integer.class); + if (mh == null) break; + mhs.add(mh); + } + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[0]); + } + private static final MethodHandle[] FILL_ARRAYS = makeFillArrays(); + + private static Object[] copyAsReferenceArray(Class arrayType, Object... a) { + return Arrays.copyOf(a, a.length, arrayType); + } + private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { + Object a = w.makeArray(boxes.length); + w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length); + return a; } - static final MethodHandle[] ARRAYS = makeArrays(); /** Return a method handle that takes the indicated number of Object * arguments and returns an Object array of them, as if for varargs. */ public static MethodHandle varargsArray(int nargs) { - if (nargs < ARRAYS.length) - return ARRAYS[nargs]; - // else need to spin bytecode or do something else fancy - throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs); + MethodHandle mh = ARRAYS[nargs]; + if (mh != null) return mh; + mh = findCollector("array", nargs, Object[].class); + if (mh != null) return ARRAYS[nargs] = mh; + MethodHandle producer = filler(0); // identity function produces result + return ARRAYS[nargs] = buildVarargsArray(producer, nargs); } + private static MethodHandle buildVarargsArray(MethodHandle producer, int nargs) { + // Build up the result mh as a sequence of fills like this: + // producer(fill(fill(fill(newArray(23),0,x1..x10),10,x11..x20),20,x21..x23)) + // The various fill(_,10*I,___*[J]) are reusable. + MethodHandle filler = filler(nargs); + MethodHandle mh = producer; + mh = MethodHandles.dropArguments(mh, 1, filler.type().parameterList()); + mh = MethodHandles.foldArguments(mh, filler); + mh = MethodHandles.foldArguments(mh, buildNewArray(nargs)); + return mh; + } + + private static MethodHandle buildNewArray(int nargs) { + return MethodHandles.insertArguments(NEW_ARRAY, 0, (int) nargs); + } + + private static final MethodHandle[] FILLERS = new MethodHandle[MAX_ARITY+1]; + // filler(N).invoke(a, arg0..arg[N-1]) fills a[0]..a[N-1] + private static MethodHandle filler(int nargs) { + MethodHandle filler = FILLERS[nargs]; + if (filler != null) return filler; + return FILLERS[nargs] = buildFiller(nargs); + } + private static MethodHandle buildFiller(int nargs) { + if (nargs == 0) + return MethodHandles.identity(Object[].class); + final int CHUNK = (FILL_ARRAYS.length - 1); + int rightLen = nargs % CHUNK; + int leftLen = nargs - rightLen; + if (rightLen == 0) { + leftLen = nargs - (rightLen = CHUNK); + if (FILLERS[leftLen] == null) { + // build some precursors from left to right + for (int j = 0; j < leftLen; j += CHUNK) filler(j); + } + } + MethodHandle leftFill = filler(leftLen); // recursive fill + MethodHandle rightFill = FILL_ARRAYS[rightLen]; + rightFill = MethodHandles.insertArguments(rightFill, 1, (int) leftLen); // [leftLen..nargs-1] + + // Combine the two fills: right(left(newArray(nargs), x1..x20), x21..x23) + MethodHandle mh = filler(0); // identity function produces result + mh = MethodHandles.dropArguments(mh, 1, rightFill.type().parameterList()); + mh = MethodHandles.foldArguments(mh, rightFill); + if (leftLen > 0) { + mh = MethodHandles.dropArguments(mh, 1, leftFill.type().parameterList()); + mh = MethodHandles.foldArguments(mh, leftFill); + } + return mh; + } + + // Type-polymorphic version of varargs maker. + private static final ClassValue TYPED_COLLECTORS + = new ClassValue() { + protected MethodHandle[] computeValue(Class type) { + return new MethodHandle[256]; + } + }; + + /** Return a method handle that takes the indicated number of + * typed arguments and returns an array of them. + * The type argument is the array type. + */ + public static MethodHandle varargsArray(Class arrayType, int nargs) { + Class elemType = arrayType.getComponentType(); + if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType); + // FIXME: Need more special casing and caching here. + if (elemType == Object.class) + return varargsArray(nargs); + // other cases: primitive arrays, subtypes of Object[] + MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); + MethodHandle mh = nargs < cache.length ? cache[nargs] : null; + if (mh != null) return mh; + MethodHandle producer = buildArrayProducer(arrayType); + mh = buildVarargsArray(producer, nargs); + mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); + cache[nargs] = mh; + return mh; + } + + private static MethodHandle buildArrayProducer(Class arrayType) { + Class elemType = arrayType.getComponentType(); + if (elemType.isPrimitive()) + return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType)); + else + return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType); + } + + // List version of varargs maker. + private static final List NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); private static List makeList(Object... args) { return Arrays.asList(args); } private static List list() { return NO_ARGS_LIST; } @@ -784,34 +1194,29 @@ public class ValueConversions { Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } - static MethodHandle[] makeLists() { - ArrayList arrays = new ArrayList(); - MethodHandles.Lookup lookup = IMPL_LOOKUP; + private static MethodHandle[] makeLists() { + ArrayList mhs = new ArrayList<>(); for (;;) { - int nargs = arrays.size(); - MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class); - String name = "list"; - MethodHandle array = null; - try { - array = lookup.findStatic(ValueConversions.class, name, type); - } catch (ReflectiveOperationException ex) { - } - if (array == null) break; - arrays.add(array); + MethodHandle mh = findCollector("list", mhs.size(), List.class); + if (mh == null) break; + mhs.add(mh); } - assert(arrays.size() == 11); // current number of methods - return arrays.toArray(new MethodHandle[0]); + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[MAX_ARITY+1]); } - static final MethodHandle[] LISTS = makeLists(); + private static final MethodHandle[] LISTS = makeLists(); /** Return a method handle that takes the indicated number of Object - * arguments and returns List. + * arguments and returns a List. */ public static MethodHandle varargsList(int nargs) { - if (nargs < LISTS.length) - return LISTS[nargs]; - // else need to spin bytecode or do something else fancy - throw new UnsupportedOperationException("NYI"); + MethodHandle mh = LISTS[nargs]; + if (mh != null) return mh; + mh = findCollector("list", nargs, List.class); + if (mh != null) return LISTS[nargs] = mh; + return LISTS[nargs] = buildVarargsList(nargs); + } + private static MethodHandle buildVarargsList(int nargs) { + return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST); } } - diff --git a/jdk/src/share/classes/sun/invoke/util/VerifyType.java b/jdk/src/share/classes/sun/invoke/util/VerifyType.java index 39286d09ca4..2970135c1b0 100644 --- a/jdk/src/share/classes/sun/invoke/util/VerifyType.java +++ b/jdk/src/share/classes/sun/invoke/util/VerifyType.java @@ -54,9 +54,15 @@ public class VerifyType { if (dst == void.class) return true; // drop any return value if (isNullType(src)) return !dst.isPrimitive(); if (!src.isPrimitive()) return dst.isAssignableFrom(src); + if (!dst.isPrimitive()) return false; // Verifier allows an int to carry byte, short, char, or even boolean: - if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt(); - return false; + Wrapper sw = Wrapper.forPrimitiveType(src); + if (dst == int.class) return sw.isSubwordOrInt(); + Wrapper dw = Wrapper.forPrimitiveType(dst); + if (!sw.isSubwordOrInt()) return false; + if (!dw.isSubwordOrInt()) return false; + if (!dw.isSigned() && sw.isSigned()) return false; + return dw.bitWidth() > sw.bitWidth(); } /** @@ -155,6 +161,7 @@ public class VerifyType { return -1; // truncation may be required if (!dw.isSigned() && sw.isSigned()) return -1; // sign elimination may be required + return 1; } if (src == float.class || dst == float.class) { if (src == double.class || dst == double.class) diff --git a/jdk/src/share/classes/sun/invoke/util/Wrapper.java b/jdk/src/share/classes/sun/invoke/util/Wrapper.java index 8e2ce578386..4b3179a3e88 100644 --- a/jdk/src/share/classes/sun/invoke/util/Wrapper.java +++ b/jdk/src/share/classes/sun/invoke/util/Wrapper.java @@ -26,37 +26,47 @@ package sun.invoke.util; public enum Wrapper { - BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)), + BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned(1)), // These must be in the order defined for widening primitive conversions in JLS 5.1.2 - BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)), - SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)), - CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)), - INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)), - LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)), - FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)), - DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)), - //NULL(Null.class, null.class, 'N', null, Format.other(1)), - OBJECT(Object.class, Object.class, 'L', null, Format.other(1)), + BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)), + SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(16)), + CHAR(Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)), + INT(Integer.class, int.class, 'I', (Integer)(int)0, new int[0], Format.signed(32)), + LONG(Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed(64)), + FLOAT(Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)), + DOUBLE(Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)), + //NULL(Null.class, null.class, 'N', null, null, Format.other(1)), + OBJECT(Object.class, Object.class, 'L', null, new Object[0], Format.other(1)), // VOID must be the last type, since it is "assignable" from any other type: - VOID(Void.class, void.class, 'V', null, Format.other(0)), + VOID(Void.class, void.class, 'V', null, null, Format.other(0)), ; private final Class wrapperType; private final Class primitiveType; private final char basicTypeChar; private final Object zero; + private final Object emptyArray; private final int format; private final String simpleName; - private Wrapper(Class wtype, Class ptype, char tchar, Object zero, int format) { + private Wrapper(Class wtype, Class ptype, char tchar, Object zero, Object emptyArray, int format) { this.wrapperType = wtype; this.primitiveType = ptype; this.basicTypeChar = tchar; this.zero = zero; + this.emptyArray = emptyArray; this.format = format; this.simpleName = wtype.getSimpleName(); } + /** For debugging, give the details of this wrapper. */ + public String detailString() { + return simpleName+ + java.util.Arrays.asList(wrapperType, primitiveType, + basicTypeChar, zero, + "0x"+Integer.toHexString(format)); + } + private static abstract class Format { static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12; static final int @@ -114,16 +124,18 @@ public enum Wrapper { public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; } /** Is the wrapped type either float or double? */ public boolean isFloating() { return format >= Format.FLOAT; } + /** Is the wrapped type either void or a reference? */ + public boolean isOther() { return (format & ~Format.SLOT_MASK) == 0; } - /** Does the JVM verifier allow a variable of this wrapper's + /** Does the JLS 5.1.2 allow a variable of this wrapper's * primitive type to be assigned from a value of the given wrapper's primitive type? * Cases: *
      *
    • unboxing followed by widening primitive conversion - *
    • any type converted to {@code void} + *
    • any type converted to {@code void} (i.e., dropping a method call's value) *
    • boxing conversion followed by widening reference conversion to {@code Object} - *
    • conversion of {@code boolean} to any type *
    + * These are the cases allowed by MethodHandle.asType and convertArguments. */ public boolean isConvertibleFrom(Wrapper source) { if (this == source) return true; @@ -131,13 +143,75 @@ public enum Wrapper { // At best, this is a narrowing conversion. return false; } - if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) { - assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT); + // All conversions are allowed in the enum order between floats and signed ints. + // First detect non-signed non-float types (boolean, char, Object, void). + boolean floatOrSigned = (((this.format & source.format) & Format.SIGNED) != 0); + if (!floatOrSigned) { + if (this.isOther()) return true; + // can convert char to int or wider, but nothing else + if (source.format == Format.CHAR) return true; + // no other conversions are classified as widening return false; } + // All signed and float conversions in the enum order are widening. + assert(this.isFloating() || this.isSigned()); + assert(source.isFloating() || source.isSigned()); return true; } + static { assert(checkConvertibleFrom()); } + private static boolean checkConvertibleFrom() { + // Check the matrix for correct classification of widening conversions. + for (Wrapper w : values()) { + assert(w.isConvertibleFrom(w)); + assert(VOID.isConvertibleFrom(w)); + if (w != VOID) { + assert(OBJECT.isConvertibleFrom(w)); + assert(!w.isConvertibleFrom(VOID)); + } + // check relations with unsigned integral types: + if (w != CHAR) { + assert(!CHAR.isConvertibleFrom(w)); + if (!w.isConvertibleFrom(INT)) + assert(!w.isConvertibleFrom(CHAR)); + } + if (w != BOOLEAN) { + assert(!BOOLEAN.isConvertibleFrom(w)); + if (w != VOID && w != OBJECT) + assert(!w.isConvertibleFrom(BOOLEAN)); + } + // check relations with signed integral types: + if (w.isSigned()) { + for (Wrapper x : values()) { + if (w == x) continue; + if (x.isFloating()) + assert(!w.isConvertibleFrom(x)); + else if (x.isSigned()) { + if (w.compareTo(x) < 0) + assert(!w.isConvertibleFrom(x)); + else + assert(w.isConvertibleFrom(x)); + } + } + } + // check relations with floating types: + if (w.isFloating()) { + for (Wrapper x : values()) { + if (w == x) continue; + if (x.isSigned()) + assert(w.isConvertibleFrom(x)); + else if (x.isFloating()) { + if (w.compareTo(x) < 0) + assert(!w.isConvertibleFrom(x)); + else + assert(w.isConvertibleFrom(x)); + } + } + } + } + return true; // i.e., assert(true) + } + /** Produce a zero value for the given wrapper type. * This will be a numeric zero for a number or character, * false for a boolean, and null for a reference or void. @@ -549,7 +623,7 @@ public enum Wrapper { } private static boolean boolValue(long bits) { - //bits &= 1; // simple 31-bit zero extension + bits &= 1; // simple 31-bit zero extension return (bits != 0); } @@ -559,4 +633,31 @@ public enum Wrapper { private static RuntimeException newIllegalArgumentException(String message) { return new IllegalArgumentException(message); } + + // primitive array support + public Object makeArray(int len) { + return java.lang.reflect.Array.newInstance(primitiveType, len); + } + public Class arrayType() { + return emptyArray.getClass(); + } + public void copyArrayUnboxing(Object[] values, int vpos, Object a, int apos, int length) { + if (a.getClass() != arrayType()) + arrayType().cast(a); // throw NPE or CCE if bad type + for (int i = 0; i < length; i++) { + Object value = values[i+vpos]; + value = convert(value, primitiveType); + java.lang.reflect.Array.set(a, i+apos, value); + } + } + public void copyArrayBoxing(Object a, int apos, Object[] values, int vpos, int length) { + if (a.getClass() != arrayType()) + arrayType().cast(a); // throw NPE or CCE if bad type + for (int i = 0; i < length; i++) { + Object value = java.lang.reflect.Array.get(a, i+apos); + //Already done: value = convert(value, primitiveType); + assert(value.getClass() == wrapperType); + values[i+vpos] = value; + } + } } diff --git a/jdk/test/java/lang/invoke/6998541/Test6998541.java b/jdk/test/java/lang/invoke/6998541/Test6998541.java new file mode 100644 index 00000000000..0f64eee4b0c --- /dev/null +++ b/jdk/test/java/lang/invoke/6998541/Test6998541.java @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2011, 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. + * + */ + +/** + * @test + * @bug 6998541 + * @summary JSR 292 implement missing return-type conversion for OP_RETYPE_RAW + * + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2 + * -DTest6998541.N=100000 -DTest6998541.KIND=cast Test6998541 + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2 + * -DTest6998541.N=100000 -DTest6998541.KIND=normal Test6998541 + */ + +import java.util.*; + +import java.lang.invoke.*; +import static java.lang.invoke.MethodHandles.*; + +public class Test6998541 { + private static final Class CLASS = Test6998541.class; + private static final String NAME = "identity"; + private static final int N = Math.max(2, Integer.getInteger(CLASS.getSimpleName()+".N", 10000)); + private static final String KIND = System.getProperty(CLASS.getSimpleName()+".KIND", "cast"); + private static final int BITS = 0x00000201; + + private static final boolean DO_CASTS = !KIND.equals("normal"); + + public static void main(String[] args) throws Throwable { + System.out.println("KIND="+KIND+" DO_CASTS="+DO_CASTS+" N="+N); + doboolean(); + dobyte(); + dochar(); + doshort(); + doint(); + dolong(); + dofloat(); + dodouble(); + dovoid(); + } + + private static void doboolean() throws Throwable { + for (int i = 0; i < N; i++) { + boolean2prim(false); + boolean2prim(true); + } + boolean2prim_invalid(true); + } + private static void dobyte() throws Throwable { + byte x = Byte.MIN_VALUE; + for (int i = 0; i < N; i++, x++) + byte2prim(x); + byte2prim_invalid(x); + } + private static void dochar() throws Throwable { + char x = Character.MIN_VALUE; + for (int i = 0; i < N; i++, x++) + char2prim(x); + char2prim_invalid(x); + } + private static void doshort() throws Throwable { + short x = Short.MIN_VALUE; + for (int i = 0; i < N; i++, x++) + short2prim(x); + short2prim_invalid(x); + } + private static void doint() throws Throwable { + int x = Integer.MIN_VALUE; + int D = Integer.MAX_VALUE / (N / 2) | BITS; + for (int i = 0; i < N; i++, x += D) { + int2prim(x); + } + int2prim_invalid(x); + } + private static void dolong() throws Throwable { + long x = Long.MIN_VALUE; + long D = Long.MAX_VALUE / ((long) (N / 2)) | BITS; + for (int i = 0; i < N; i++, x += D) + long2prim(x); + long2prim_invalid(x); + } + private static void dofloat() throws Throwable { + float x = Float.MIN_VALUE; + float D = Float.MAX_VALUE / ((float) (N / 2)); + for (int i = 0; i < N; i++, x += D) + float2prim(x); + float2prim_invalid(x); + } + private static void dodouble() throws Throwable { + double x = Double.MIN_VALUE; + double D = Double.MAX_VALUE / ((double) (N / 2)); + for (int i = 0; i < N; i++, x += D) + double2prim(x); + double2prim_invalid(x); + } + private static void dovoid() throws Throwable { + for (int i = 0; i < N; i++) { + void2prim(i); + } + void2prim_invalid(0); + // do the other direction here also: + for (int i = 0; i < N; i++) { + prim2void(i); + } + prim2void_invalid(0); + } + + private static void assertEquals(Object o, Object o2) { + if (!o.equals(o2)) + throw new AssertionError("expected: " + o + ", found: " + o2); + } + private static void fail() { + throw new AssertionError(); + } + + private final static MethodHandles.Lookup lookup = MethodHandles.lookup(); + + private static MethodHandle mh(Class ret, Class... args) { + try { + MethodType mt = MethodType.methodType(ret, args); + Class lookupRet = (args.length == 0 ? void.class : args[0]); + MethodHandle mh = lookup.findStatic(CLASS, NAME, mt.changeReturnType(lookupRet)); + if (DO_CASTS) + return MethodHandles.explicitCastArguments(mh, mt); + if (canDoAsType(mh.type(), mt)) + return mh.asType(mt); + try { + mh.asType(mt); + throw new AssertionError("asType should not succeed: "+mh+" => "+mt); + } catch (WrongMethodTypeException ex) { + // this was a required WMTE + return mh.asType(mt.generic()).asType(mt); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + private static final Class[] NUMERIC_TYPE_WIDENING_ORDER = { + byte.class, short.class, int.class, long.class, float.class, double.class + }; + private static boolean canDoAsType(Class src, Class dst) { + if (src == dst) return true; + if (dst == void.class) return true; + if (!src.isPrimitive() || !dst.isPrimitive()) return true; + // primitive conversion works for asType only when it's widening + if (src == boolean.class || dst == boolean.class) return false; + if (dst == char.class) return false; + if (src == char.class) src = int.class; // can widen char to int + for (Class ntype : NUMERIC_TYPE_WIDENING_ORDER) { + if (src == ntype) return true; + if (dst == ntype) return false; + } + throw new AssertionError("should not reach here: "+src+", "+dst); + } + private static boolean canDoAsType(MethodType mt0, MethodType mt1) { + Class rt0 = mt0.returnType(); + Class rt1 = mt1.returnType(); + if (!canDoAsType(rt0, rt1)) return false; + int argc = mt0.parameterCount(); + if (argc != mt1.parameterCount()) return false; + for (int i = 0; i < argc; i++) { + if (!canDoAsType(mt1.parameterType(i), mt0.parameterType(i))) + return false; + } + return true; + } + + private static MethodHandle mh_z(Class ret) { return mh(ret, boolean.class); } + + private final static MethodHandle mh_zz = mh_z(boolean.class); + private final static MethodHandle mh_bz = mh_z(byte.class ); + private final static MethodHandle mh_cz = mh_z(char.class ); + private final static MethodHandle mh_sz = mh_z(short.class ); + private final static MethodHandle mh_iz = mh_z(int.class ); + private final static MethodHandle mh_jz = mh_z(long.class ); + private final static MethodHandle mh_fz = mh_z(float.class ); + private final static MethodHandle mh_dz = mh_z(double.class ); + + private static void boolean2prim(boolean x) throws Throwable { + int i = x ? 1 : 0; + assertEquals( x, (boolean) mh_zz.invokeExact(x)); // boolean -> boolean + if (!DO_CASTS) return; + assertEquals((byte) i, (byte) mh_bz.invokeExact(x)); // boolean -> byte + assertEquals((char) i, (char) mh_cz.invokeExact(x)); // boolean -> char + assertEquals((short) i, (short) mh_sz.invokeExact(x)); // boolean -> short + assertEquals((int) i, (int) mh_iz.invokeExact(x)); // boolean -> int + assertEquals((long) i, (long) mh_jz.invokeExact(x)); // boolean -> long + assertEquals((float) i, (float) mh_fz.invokeExact(x)); // boolean -> float + assertEquals((double) i, (double) mh_dz.invokeExact(x)); // boolean -> double + } + private static void boolean2prim_invalid(boolean x) throws Throwable { + if (DO_CASTS) return; + try { byte y = (byte) mh_bz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> byte + try { char y = (char) mh_cz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> char + try { short y = (short) mh_sz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> short + try { int y = (int) mh_iz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> int + try { long y = (long) mh_jz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> long + try { float y = (float) mh_fz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> float + try { double y = (double) mh_dz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> double + } + + private static MethodHandle mh_b(Class ret) { return mh(ret, byte.class); } + + private final static MethodHandle mh_zb = mh_b(boolean.class); + private final static MethodHandle mh_bb = mh_b(byte.class ); + private final static MethodHandle mh_cb = mh_b(char.class ); + private final static MethodHandle mh_sb = mh_b(short.class ); + private final static MethodHandle mh_ib = mh_b(int.class ); + private final static MethodHandle mh_jb = mh_b(long.class ); + private final static MethodHandle mh_fb = mh_b(float.class ); + private final static MethodHandle mh_db = mh_b(double.class ); + + private static void byte2prim(byte x) throws Throwable { + assertEquals((byte) x, (byte) mh_bb.invokeExact(x)); // byte -> byte + assertEquals((short) x, (short) mh_sb.invokeExact(x)); // byte -> short + assertEquals((int) x, (int) mh_ib.invokeExact(x)); // byte -> int + assertEquals((long) x, (long) mh_jb.invokeExact(x)); // byte -> long + assertEquals((float) x, (float) mh_fb.invokeExact(x)); // byte -> float + assertEquals((double) x, (double) mh_db.invokeExact(x)); // byte -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((char) x, (char) mh_cb.invokeExact(x)); // byte -> char + assertEquals((boolean) z, (boolean) mh_zb.invokeExact(x)); // byte -> boolean + } + private static void byte2prim_invalid(byte x) throws Throwable { + if (DO_CASTS) return; + try { char y = (char) mh_cb.invokeExact(x); fail(); } catch (ClassCastException _) {} // byte -> char + try { boolean y = (boolean) mh_zb.invokeExact(x); fail(); } catch (ClassCastException _) {} // byte -> boolean + } + + private static MethodHandle mh_c(Class ret) { return mh(ret, char.class); } + + private final static MethodHandle mh_zc = mh_c(boolean.class); + private final static MethodHandle mh_bc = mh_c(byte.class ); + private final static MethodHandle mh_cc = mh_c(char.class ); + private final static MethodHandle mh_sc = mh_c(short.class ); + private final static MethodHandle mh_ic = mh_c(int.class ); + private final static MethodHandle mh_jc = mh_c(long.class ); + private final static MethodHandle mh_fc = mh_c(float.class ); + private final static MethodHandle mh_dc = mh_c(double.class ); + + private static void char2prim(char x) throws Throwable { + assertEquals((char) x, (char) mh_cc.invokeExact(x)); // char -> char + assertEquals((int) x, (int) mh_ic.invokeExact(x)); // char -> int + assertEquals((long) x, (long) mh_jc.invokeExact(x)); // char -> long + assertEquals((float) x, (float) mh_fc.invokeExact(x)); // char -> float + assertEquals((double) x, (double) mh_dc.invokeExact(x)); // char -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zc.invokeExact(x)); // char -> boolean + assertEquals((byte) x, (byte) mh_bc.invokeExact(x)); // char -> byte + assertEquals((short) x, (short) mh_sc.invokeExact(x)); // char -> short + } + private static void char2prim_invalid(char x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> boolean + try { byte y = (byte) mh_bc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> byte + try { short y = (short) mh_sc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> short + } + + private static MethodHandle mh_s(Class ret) { return mh(ret, short.class); } + + private final static MethodHandle mh_zs = mh_s(boolean.class); + private final static MethodHandle mh_bs = mh_s(byte.class ); + private final static MethodHandle mh_cs = mh_s(char.class ); + private final static MethodHandle mh_ss = mh_s(short.class ); + private final static MethodHandle mh_is = mh_s(int.class ); + private final static MethodHandle mh_js = mh_s(long.class ); + private final static MethodHandle mh_fs = mh_s(float.class ); + private final static MethodHandle mh_ds = mh_s(double.class ); + + private static void short2prim(short x) throws Throwable { + assertEquals((short) x, (short) mh_ss.invokeExact(x)); // short -> short + assertEquals((int) x, (int) mh_is.invokeExact(x)); // short -> int + assertEquals((long) x, (long) mh_js.invokeExact(x)); // short -> long + assertEquals((float) x, (float) mh_fs.invokeExact(x)); // short -> float + assertEquals((double) x, (double) mh_ds.invokeExact(x)); // short -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zs.invokeExact(x)); // short -> boolean + assertEquals((byte) x, (byte) mh_bs.invokeExact(x)); // short -> byte + assertEquals((char) x, (char) mh_cs.invokeExact(x)); // short -> char + } + private static void short2prim_invalid(short x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> boolean + try { byte y = (byte) mh_bs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> byte + try { char y = (char) mh_cs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> char + } + + private static MethodHandle mh_i(Class ret) { return mh(ret, int.class); } + + private final static MethodHandle mh_zi = mh_i(boolean.class); + private final static MethodHandle mh_bi = mh_i(byte.class ); + private final static MethodHandle mh_ci = mh_i(char.class ); + private final static MethodHandle mh_si = mh_i(short.class ); + private final static MethodHandle mh_ii = mh_i(int.class ); + private final static MethodHandle mh_ji = mh_i(long.class ); + private final static MethodHandle mh_fi = mh_i(float.class ); + private final static MethodHandle mh_di = mh_i(double.class ); + + private static void int2prim(int x) throws Throwable { + assertEquals((int) x, (int) mh_ii.invokeExact(x)); // int -> int + assertEquals((long) x, (long) mh_ji.invokeExact(x)); // int -> long + assertEquals((float) x, (float) mh_fi.invokeExact(x)); // int -> float + assertEquals((double) x, (double) mh_di.invokeExact(x)); // int -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zi.invokeExact(x)); // int -> boolean + assertEquals((byte) x, (byte) mh_bi.invokeExact(x)); // int -> byte + assertEquals((char) x, (char) mh_ci.invokeExact(x)); // int -> char + assertEquals((short) x, (short) mh_si.invokeExact(x)); // int -> short + } + private static void int2prim_invalid(int x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zi.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> boolean + try { byte y = (byte) mh_bi.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> byte + try { char y = (char) mh_ci.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> char + try { short y = (short) mh_si.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> short + } + + private static MethodHandle mh_j(Class ret) { return mh(ret, long.class); } + + private final static MethodHandle mh_zj = mh_j(boolean.class); + private final static MethodHandle mh_bj = mh_j(byte.class ); + private final static MethodHandle mh_cj = mh_j(char.class ); + private final static MethodHandle mh_sj = mh_j(short.class ); + private final static MethodHandle mh_ij = mh_j(int.class ); + private final static MethodHandle mh_jj = mh_j(long.class ); + private final static MethodHandle mh_fj = mh_j(float.class ); + private final static MethodHandle mh_dj = mh_j(double.class ); + + private static void long2prim(long x) throws Throwable { + assertEquals((long) x, (long) mh_jj.invokeExact(x)); // long -> long + assertEquals((float) x, (float) mh_fj.invokeExact(x)); // long -> float + assertEquals((double) x, (double) mh_dj.invokeExact(x)); // long -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((boolean)z, (boolean) mh_zj.invokeExact(x)); // long -> boolean + assertEquals((byte) x, (byte) mh_bj.invokeExact(x)); // long -> byte + assertEquals((char) x, (char) mh_cj.invokeExact(x)); // long -> char + assertEquals((short) x, (short) mh_sj.invokeExact(x)); // long -> short + assertEquals((int) x, (int) mh_ij.invokeExact(x)); // long -> int + } + private static void long2prim_invalid(long x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> boolean + try { byte y = (byte) mh_bj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> byte + try { char y = (char) mh_cj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> char + try { short y = (short) mh_sj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> short + try { int y = (int) mh_ij.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> int + } + + private static MethodHandle mh_f(Class ret) { return mh(ret, float.class); } + + private final static MethodHandle mh_zf = mh_f(boolean.class); + private final static MethodHandle mh_bf = mh_f(byte.class ); + private final static MethodHandle mh_cf = mh_f(char.class ); + private final static MethodHandle mh_sf = mh_f(short.class ); + private final static MethodHandle mh_if = mh_f(int.class ); + private final static MethodHandle mh_jf = mh_f(long.class ); + private final static MethodHandle mh_ff = mh_f(float.class ); + private final static MethodHandle mh_df = mh_f(double.class ); + + private static void float2prim(float x) throws Throwable { + assertEquals((float) x, (float) mh_ff.invokeExact(x)); // float -> float + assertEquals((double) x, (double) mh_df.invokeExact(x)); // float -> double + if (!DO_CASTS) return; + boolean z = (((byte) x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zf.invokeExact(x)); // float -> boolean + assertEquals((byte) x, (byte) mh_bf.invokeExact(x)); // float -> byte + assertEquals((char) x, (char) mh_cf.invokeExact(x)); // float -> char + assertEquals((short) x, (short) mh_sf.invokeExact(x)); // float -> short + assertEquals((int) x, (int) mh_if.invokeExact(x)); // float -> int + assertEquals((long) x, (long) mh_jf.invokeExact(x)); // float -> long + } + private static void float2prim_invalid(float x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> boolean + try { byte y = (byte) mh_bf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> byte + try { char y = (char) mh_cf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> char + try { short y = (short) mh_sf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> short + try { int y = (int) mh_if.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> int + try { long y = (long) mh_jf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> long + } + + private static MethodHandle mh_d(Class ret) { return mh(ret, double.class); } + + private final static MethodHandle mh_zd = mh_d(boolean.class); + private final static MethodHandle mh_bd = mh_d(byte.class ); + private final static MethodHandle mh_cd = mh_d(char.class ); + private final static MethodHandle mh_sd = mh_d(short.class ); + private final static MethodHandle mh_id = mh_d(int.class ); + private final static MethodHandle mh_jd = mh_d(long.class ); + private final static MethodHandle mh_fd = mh_d(float.class ); + private final static MethodHandle mh_dd = mh_d(double.class ); + + private static void double2prim(double x) throws Throwable { + assertEquals((double) x, (double) mh_dd.invokeExact(x)); // double -> double + if (!DO_CASTS) return; + boolean z = (((byte) x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zd.invokeExact(x)); // double -> boolean + assertEquals((byte) x, (byte) mh_bd.invokeExact(x)); // double -> byte + assertEquals((char) x, (char) mh_cd.invokeExact(x)); // double -> char + assertEquals((short) x, (short) mh_sd.invokeExact(x)); // double -> short + assertEquals((int) x, (int) mh_id.invokeExact(x)); // double -> int + assertEquals((long) x, (long) mh_jd.invokeExact(x)); // double -> long + assertEquals((float) x, (float) mh_fd.invokeExact(x)); // double -> float + } + private static void double2prim_invalid(double x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> boolean + try { byte y = (byte) mh_bd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> byte + try { char y = (char) mh_cd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> char + try { short y = (short) mh_sd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> short + try { int y = (int) mh_id.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> int + try { long y = (long) mh_jd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> long + try { float y = (float) mh_fd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> float + } + + private final static MethodHandle mh_zv = mh(boolean.class); + private final static MethodHandle mh_bv = mh(byte.class ); + private final static MethodHandle mh_cv = mh(char.class ); + private final static MethodHandle mh_sv = mh(short.class ); + private final static MethodHandle mh_iv = mh(int.class ); + private final static MethodHandle mh_jv = mh(long.class ); + private final static MethodHandle mh_fv = mh(float.class ); + private final static MethodHandle mh_dv = mh(double.class ); + + private static void void2prim(int i) throws Throwable { + if (!DO_CASTS) return; + assertEquals( false, (boolean) mh_zv.invokeExact()); // void -> boolean + assertEquals((byte) 0, (byte) mh_bv.invokeExact()); // void -> byte + assertEquals((char) 0, (char) mh_cv.invokeExact()); // void -> char + assertEquals((short) 0, (short) mh_sv.invokeExact()); // void -> short + assertEquals( 0, (int) mh_iv.invokeExact()); // void -> int + assertEquals( 0L, (long) mh_jv.invokeExact()); // void -> long + assertEquals( 0.0f, (float) mh_fv.invokeExact()); // void -> float + assertEquals( 0.0d, (double) mh_dv.invokeExact()); // void -> double + } + + private static void void2prim_invalid(double x) throws Throwable { + if (DO_CASTS) return; + try { assertEquals( false, (boolean) mh_zv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> boolean + try { assertEquals((byte) 0, (byte) mh_bv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> byte + try { assertEquals((char) 0, (char) mh_cv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> char + try { assertEquals((short) 0, (short) mh_sv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> short + try { assertEquals( 0, (int) mh_iv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> int + try { assertEquals( 0L, (long) mh_jv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> long + try { assertEquals( 0.0f, (float) mh_fv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> float + try { assertEquals( 0.0d, (double) mh_dv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> double + } + + private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); } + + private final static MethodHandle mh_vz = mh_v(boolean.class); + private final static MethodHandle mh_vb = mh_v(byte.class ); + private final static MethodHandle mh_vc = mh_v(char.class ); + private final static MethodHandle mh_vs = mh_v(short.class ); + private final static MethodHandle mh_vi = mh_v(int.class ); + private final static MethodHandle mh_vj = mh_v(long.class ); + private final static MethodHandle mh_vf = mh_v(float.class ); + private final static MethodHandle mh_vd = mh_v(double.class ); + + private static void prim2void(int x) throws Throwable { + boolean z = ((x & 1) != 0); + mh_vz.invokeExact( z); // boolean -> void + mh_vb.invokeExact((byte) x); // byte -> void + mh_vc.invokeExact((char) x); // char -> void + mh_vs.invokeExact((short) x); // short -> void + mh_vi.invokeExact((int) x); // int -> void + mh_vj.invokeExact((long) x); // long -> void + mh_vf.invokeExact((float) x); // float -> void + mh_vd.invokeExact((double) x); // double -> void + } + + private static void prim2void_invalid(int x) throws Throwable { + // no cases + } + + private static boolean identity(boolean v) { return v; } + private static byte identity(byte v) { return v; } + private static char identity(char v) { return v; } + private static short identity(short v) { return v; } + private static int identity(int v) { return v; } + private static long identity(long v) { return v; } + private static float identity(float v) { return v; } + private static double identity(double v) { return v; } + private static void identity() {} +} diff --git a/jdk/test/java/lang/invoke/InvokeGenericTest.java b/jdk/test/java/lang/invoke/InvokeGenericTest.java index 792f51833f9..8ced6c66582 100644 --- a/jdk/test/java/lang/invoke/InvokeGenericTest.java +++ b/jdk/test/java/lang/invoke/InvokeGenericTest.java @@ -53,6 +53,10 @@ public class InvokeGenericTest { if (vstr != null) verbosity = Integer.parseInt(vstr); } + public static void main(String... av) throws Throwable { + new InvokeGenericTest().testFirst(); + } + @Test public void testFirst() throws Throwable { verbosity += 9; try { @@ -103,7 +107,7 @@ public class InvokeGenericTest { void startTest(String name) { if (testName != null) printCounts(); if (verbosity >= 1) - System.out.println(name); + System.out.println("["+name+"]"); posTests = negTests = 0; testName = name; } @@ -355,6 +359,18 @@ public class InvokeGenericTest { assertEquals(Arrays.asList(args), res); } + @Test + public void testSimplePrims() throws Throwable { + startTest("testSimplePrims"); + countTest(); + int[] args = { 1, 2 }; + MethodHandle mh = callable(Object.class, Object.class); + Object res; List resl; + res = resl = (List) mh.invoke(args[0], args[1]); + //System.out.println(res); + assertEquals(Arrays.toString(args), res.toString()); + } + @Test public void testAlternateName() throws Throwable { startTest("testAlternateName"); @@ -415,9 +431,9 @@ public class InvokeGenericTest { } catch (WrongMethodTypeException ex) { return; } catch (Exception ex) { - throw new RuntimeException("wrong exception calling "+target+target.type()+" on "+Arrays.asList(args)+" : "+ex); + throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex); } - throw new RuntimeException("bad success calling "+target+target.type()+" on "+Arrays.asList(args)); + throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args)); } /** Make a list of all combinations of the given types, with the given arities. diff --git a/jdk/test/java/lang/invoke/MethodHandlesTest.java b/jdk/test/java/lang/invoke/MethodHandlesTest.java index 0ab9f192a24..9ebe37a3b96 100644 --- a/jdk/test/java/lang/invoke/MethodHandlesTest.java +++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java @@ -105,24 +105,6 @@ public class MethodHandlesTest { public MethodHandlesTest() { } - @Before - public void checkImplementedPlatform() { - boolean platformOK = false; - Properties properties = System.getProperties(); - String vers = properties.getProperty("java.vm.version"); - String name = properties.getProperty("java.vm.name"); - String arch = properties.getProperty("os.arch"); - if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") || - arch.equals("sparc") || arch.equals("sparcv9")) && - (name.contains("Client") || name.contains("Server")) - ) { - platformOK = true; - } else { - System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch)); - } - assumeTrue(platformOK); - } - String testName; static int allPosTests, allNegTests; int posTests, negTests; @@ -331,6 +313,9 @@ public class MethodHandlesTest { static MethodHandle varargsArray(int arity) { return ValueConversions.varargsArray(arity); } + static MethodHandle varargsArray(Class arrayType, int arity) { + return ValueConversions.varargsArray(arrayType, arity); + } /** Variation of varargsList, but with the given rtype. */ static MethodHandle varargsList(int arity, Class rtype) { MethodHandle list = varargsList(arity); @@ -865,7 +850,7 @@ public class MethodHandlesTest { Class type = (Class) t[1]; Object value; Field field; - try { + try { field = HasFields.class.getDeclaredField(name); } catch (Exception ex) { throw new InternalError("no field HasFields."+name); @@ -1144,16 +1129,9 @@ public class MethodHandlesTest { : MethodHandles.arrayElementSetter(arrayType); assertSame(mh.type(), expType); if (elemType != int.class && elemType != boolean.class) { - MethodType gtype; - if (true) { // FIXME: remove this path (and remove below in the mh.invokes) - gtype = mh.type().changeParameterType(0, Object.class); - if (testSetter) - gtype = gtype.changeParameterType(2, Object.class); - else - gtype = gtype.changeReturnType(Object.class); - } else - // FIXME: This simpler path hits a bug in convertArguments => ToGeneric - gtype = mh.type().generic().changeParameterType(1, int.class); + // FIXME: change Integer.class and (Integer) below to int.class and (int) below. + MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class); + if (testSetter) gtype = gtype.changeReturnType(void.class); mh = MethodHandles.convertArguments(mh, gtype); } Object sawValue, expValue; @@ -1169,7 +1147,7 @@ public class MethodHandlesTest { else if (elemType == boolean.class) mh.invokeExact((boolean[]) array, i, (boolean)random); else - mh.invokeExact(array, i, random); + mh.invokeExact(array, (Integer)i, random); assertEquals(model, array2list(array)); } else { Array.set(array, i, random); @@ -1189,7 +1167,7 @@ public class MethodHandlesTest { else if (elemType == boolean.class) sawValue = (boolean) mh.invokeExact((boolean[]) array, i); else - sawValue = mh.invokeExact(array, i); + sawValue = mh.invokeExact(array, (Integer)i); assertEquals(sawValue, expValue); assertEquals(model, array2list(array)); } @@ -1341,21 +1319,15 @@ public class MethodHandlesTest { int numcases = 1; for (int outargs = 0; outargs <= max; outargs++) { if (outargs - inargs >= MAX_ARG_INCREASE) continue; - int[] reorder = new int[outargs]; int casStep = dilution + 1; // Avoid some common factors: while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) || (casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0)) casStep++; - for (int cas = 0; cas < numcases; cas += casStep) { - for (int i = 0, c = cas; i < outargs; i++) { - reorder[i] = c % inargs; - c /= inargs; - } - testPermuteArguments(args, types, reorder); - } + testPermuteArguments(args, types, outargs, numcases, casStep); numcases *= inargs; if (dilution > 10 && outargs >= 4) { + int[] reorder = new int[outargs]; // Do some special patterns, which we probably missed. // Replication of a single argument or argument pair. for (int i = 0; i < inargs; i++) { @@ -1383,6 +1355,19 @@ public class MethodHandlesTest { } } + public void testPermuteArguments(Object[] args, Class[] types, + int outargs, int numcases, int casStep) throws Throwable { + int inargs = args.length; + int[] reorder = new int[outargs]; + for (int cas = 0; cas < numcases; cas += casStep) { + for (int i = 0, c = cas; i < outargs; i++) { + reorder[i] = c % inargs; + c /= inargs; + } + testPermuteArguments(args, types, reorder); + } + } + static int[] reverse(int[] reorder) { reorder = reorder.clone(); for (int i = 0, imax = reorder.length / 2; i < imax; i++) { @@ -1433,6 +1418,12 @@ public class MethodHandlesTest { MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder); Object result = newTarget.invokeWithArguments(args); Object expected = Arrays.asList(permArgs); + if (!expected.equals(result)) { + System.out.println("*** failed permuteArguments "+Arrays.toString(reorder)+" types="+Arrays.asList(types)); + System.out.println("in args: "+Arrays.asList(args)); + System.out.println("out args: "+expected); + System.out.println("bad args: "+result); + } assertEquals(expected, result); } @@ -1456,26 +1447,27 @@ public class MethodHandlesTest { } public void testSpreadArguments(Class argType, int pos, int nargs) throws Throwable { countTest(); - MethodHandle target = varargsArray(nargs); - MethodHandle target2 = changeArgTypes(target, argType); + Class arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); + MethodHandle target2 = varargsArray(arrayType, nargs); + MethodHandle target = target2.asType(target2.type().generic()); if (verbosity >= 3) System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); Object[] args = randomArgs(target2.type().parameterArray()); // make sure the target does what we think it does: - if (pos == 0 && nargs < 5) { - Object[] check = (Object[]) target.invokeWithArguments(args); + if (pos == 0 && nargs < 5 && !argType.isPrimitive()) { + Object[] check = (Object[]) (Object) target.invokeWithArguments(args); assertArrayEquals(args, check); switch (nargs) { case 0: - check = (Object[]) target.invokeExact(); + check = (Object[]) (Object) target.invokeExact(); assertArrayEquals(args, check); break; case 1: - check = (Object[]) target.invokeExact(args[0]); + check = (Object[]) (Object) target.invokeExact(args[0]); assertArrayEquals(args, check); break; case 2: - check = (Object[]) target.invokeExact(args[0], args[1]); + check = (Object[]) (Object) target.invokeExact(args[0], args[1]); assertArrayEquals(args, check); break; } @@ -1483,23 +1475,50 @@ public class MethodHandlesTest { List> newParams = new ArrayList>(target2.type().parameterList()); { // modify newParams in place List> spreadParams = newParams.subList(pos, nargs); - spreadParams.clear(); spreadParams.add(Object[].class); + spreadParams.clear(); spreadParams.add(arrayType); } - MethodType newType = MethodType.methodType(Object.class, newParams); - MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType); - Object[] returnValue; + MethodType newType = MethodType.methodType(arrayType, newParams); + MethodHandle result = target2.asSpreader(arrayType, nargs-pos); + assert(result.type() == newType) : Arrays.asList(result, newType); + result = result.asType(newType.generic()); + Object returnValue; if (pos == 0) { - // In the following line, the first cast implies - // normal Object return value for the MH call (Object[])->Object, - // while the second cast dynamically converts to an Object array. - // Such a double cast is typical of MH.invokeExact. - returnValue = (Object[]) (Object) result.invokeExact(args); + Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); + returnValue = result.invokeExact(args2); } else { Object[] args1 = Arrays.copyOfRange(args, 0, pos+1); - args1[pos] = Arrays.copyOfRange(args, pos, args.length); - returnValue = (Object[]) result.invokeWithArguments(args1); + args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); + returnValue = result.invokeWithArguments(args1); + } + String argstr = Arrays.toString(args); + if (!argType.isPrimitive()) { + Object[] rv = (Object[]) returnValue; + String rvs = Arrays.toString(rv); + if (!Arrays.equals(args, rv)) { + System.out.println("method: "+result); + System.out.println("expected: "+argstr); + System.out.println("returned: "+rvs); + assertArrayEquals(args, rv); + } + } else if (argType == int.class) { + String rvs = Arrays.toString((int[]) returnValue); + if (!argstr.equals(rvs)) { + System.out.println("method: "+result); + System.out.println("expected: "+argstr); + System.out.println("returned: "+rvs); + assertEquals(argstr, rvs); + } + } else if (argType == long.class) { + String rvs = Arrays.toString((long[]) returnValue); + if (!argstr.equals(rvs)) { + System.out.println("method: "+result); + System.out.println("expected: "+argstr); + System.out.println("returned: "+rvs); + assertEquals(argstr, rvs); + } + } else { + // cannot test... } - assertArrayEquals(args, returnValue); } @Test @@ -2130,15 +2149,12 @@ public class MethodHandlesTest { Object z = surprise.invokeExact(x); System.out.println("Failed to throw; got z="+z); assertTrue(false); - } catch (Exception ex) { + } catch (ClassCastException ex) { if (verbosity > 2) System.out.println("caught "+ex); if (verbosity > 3) ex.printStackTrace(); - assertTrue(ex instanceof ClassCastException - // FIXME: accept only one of the two for any given unit test - || ex instanceof WrongMethodTypeException - ); + assertTrue(true); // all is well } } @@ -2328,6 +2344,34 @@ class ValueConversions { // else need to spin bytecode or do something else fancy throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs); } + public static MethodHandle varargsArray(Class arrayType, int nargs) { + Class elemType = arrayType.getComponentType(); + MethodType vaType = MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType)); + MethodHandle mh = varargsArray(nargs); + if (arrayType != Object[].class) + mh = MethodHandles.filterReturnValue(mh, CHANGE_ARRAY_TYPE.bindTo(arrayType)); + return mh.asType(vaType); + } + static Object changeArrayType(Class arrayType, Object[] a) { + Class elemType = arrayType.getComponentType(); + if (!elemType.isPrimitive()) + return Arrays.copyOf(a, a.length, arrayType.asSubclass(Object[].class)); + Object b = java.lang.reflect.Array.newInstance(elemType, a.length); + for (int i = 0; i < a.length; i++) + java.lang.reflect.Array.set(b, i, a[i]); + return b; + } + private static final MethodHandle CHANGE_ARRAY_TYPE; + static { + try { + CHANGE_ARRAY_TYPE = IMPL_LOOKUP.findStatic(ValueConversions.class, "changeArrayType", + MethodType.methodType(Object.class, Class.class, Object[].class)); + } catch (NoSuchMethodException | IllegalAccessException ex) { + Error err = new InternalError("uncaught exception"); + err.initCause(ex); + throw err; + } + } private static final List NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); private static List makeList(Object... args) { return Arrays.asList(args); } diff --git a/jdk/test/java/lang/invoke/RicochetTest.java b/jdk/test/java/lang/invoke/RicochetTest.java new file mode 100644 index 00000000000..310b576ddc8 --- /dev/null +++ b/jdk/test/java/lang/invoke/RicochetTest.java @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* @test + * @summary unit tests for recursive method handles + * @run junit/othervm -DRicochetTest.MAX_ARITY=255 test.java.lang.invoke.RicochetTest + */ + +package test.java.lang.invoke; + +import java.lang.invoke.*; +import java.util.*; +import org.junit.*; +import static java.lang.invoke.MethodType.*; +import static java.lang.invoke.MethodHandles.*; +import static org.junit.Assert.*; +import static org.junit.Assume.*; + + +/** + * + * @author jrose + */ +public class RicochetTest { + private static final Class CLASS = RicochetTest.class; + private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40); + + public static void main(String... av) throws Throwable { + RicochetTest test = new RicochetTest(); + if (av.length > 0) test.testOnly = Arrays.asList(av).toString(); + if (REPEAT == 1 || test.testOnly != null) { + test.testAll(); + if (test.testOnlyTests == null) throw new RuntimeException("no matching test: "+test.testOnly); + } else if (REPEAT == 0) { + org.junit.runner.JUnitCore.runClasses(RicochetTest.class); + } else { + verbose(1, "REPEAT="+REPEAT); + for (int i = 0; i < REPEAT; i++) { + test.testRepetition = (i+1); + verbose(0, "[#"+test.testRepetition+"]"); + test.testAll(); + } + } + } + int testRepetition; + + public void testAll() throws Throwable { + testNull(); + testBoxInteger(); + testFilterReturnValue(); + testFilterObject(); + testBoxLong(); + testFilterInteger(); + testIntSpreads(); + testByteSpreads(); + testLongSpreads(); + testIntCollects(); + testReturns(); + } + + @Test + public void testNull() throws Throwable { + if (testRepetition > (1+REPEAT/100)) return; // trivial test + if (!startTest("testNull")) return; + assertEquals(opI(37), opI.invokeWithArguments(37)); + assertEqualFunction(opI, opI); + } + + @Test + public void testBoxInteger() throws Throwable { + if (!startTest("testBoxInteger")) return; + assertEqualFunction(opI, opI.asType(opL_I.type()).asType(opI.type())); + } + + @Test + public void testFilterReturnValue() throws Throwable { + if (!startTest("testFilterReturnValue")) return; + int[] ints = { 12, 23, 34, 45, 56, 67, 78, 89 }; + Object res = list8ints.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]); + assertEquals(Arrays.toString(ints), res.toString()); + MethodHandle idreturn = filterReturnValue(list8ints, identity(Object.class)); + res = idreturn.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]); + assertEquals(Arrays.toString(ints), res.toString()); + MethodHandle add0 = addL.bindTo(0); + assertEqualFunction(filterReturnValue(opL2, add0), opL2); + } + + @Test + public void testFilterObject() throws Throwable { + if (!startTest("testFilterObject")) return; + MethodHandle add0 = addL.bindTo(0); + assertEqualFunction(sequence(opL2, add0), opL2); + int bump13 = -13; // value near 20 works as long as test values are near [-80..80] + MethodHandle add13 = addL.bindTo(bump13); + MethodHandle add13_0 = addL.bindTo(opI2(bump13, 0)); + MethodHandle add13_1 = addL.bindTo(opI2(0, bump13)); + assertEqualFunction(sequence(opL2, add13_0), + filterArguments(opL2, 0, add13)); + assertEqualFunction(sequence(opL2, add13_1), + filterArguments(opL2, 1, add13)); + System.out.println("[testFilterObject done]"); + } + + @Test + public void testBoxLong() throws Throwable { + if (!startTest("testBoxLong")) return; + assertEqualFunction(opJ, opJ.asType(opL_J.type()).asType(opJ.type())); + } + + @Test + public void testFilterInteger() throws Throwable { + if (!startTest("testFilterInteger")) return; + assertEqualFunction(opI, sequence(convI_L, opL_I)); + } + + @Test + public void testIntSpreads() throws Throwable { + if (!startTest("testIntSpreads")) return; + MethodHandle id = identity(int[].class); + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = 0; nargs <= MAX; nargs++) { + if (nargs > 30 && nargs < MAX-20) nargs += 10; + int[] args = new int[nargs]; + for (int j = 0; j < args.length; j++) args[j] = (int)(j + 11); + //System.out.println("testIntSpreads "+Arrays.toString(args)); + int[] args1 = (int[]) id.invokeExact(args); + assertArrayEquals(args, args1); + MethodHandle coll = id.asCollector(int[].class, nargs); + int[] args2 = args; + switch (nargs) { + case 0: args2 = (int[]) coll.invokeExact(); break; + case 1: args2 = (int[]) coll.invokeExact(args[0]); break; + case 2: args2 = (int[]) coll.invokeExact(args[0], args[1]); break; + case 3: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2]); break; + case 4: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break; + case 5: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break; + } + assertArrayEquals(args, args2); + MethodHandle mh = coll.asSpreader(int[].class, nargs); + int[] args3 = (int[]) mh.invokeExact(args); + assertArrayEquals(args, args3); + } + } + + @Test + public void testByteSpreads() throws Throwable { + if (!startTest("testByteSpreads")) return; + MethodHandle id = identity(byte[].class); + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = 0; nargs <= MAX; nargs++) { + if (nargs > 30 && nargs < MAX-20) nargs += 10; + byte[] args = new byte[nargs]; + for (int j = 0; j < args.length; j++) args[j] = (byte)(j + 11); + //System.out.println("testByteSpreads "+Arrays.toString(args)); + byte[] args1 = (byte[]) id.invokeExact(args); + assertArrayEquals(args, args1); + MethodHandle coll = id.asCollector(byte[].class, nargs); + byte[] args2 = args; + switch (nargs) { + case 0: args2 = (byte[]) coll.invokeExact(); break; + case 1: args2 = (byte[]) coll.invokeExact(args[0]); break; + case 2: args2 = (byte[]) coll.invokeExact(args[0], args[1]); break; + case 3: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2]); break; + case 4: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break; + case 5: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break; + } + assertArrayEquals(args, args2); + MethodHandle mh = coll.asSpreader(byte[].class, nargs); + byte[] args3 = (byte[]) mh.invokeExact(args); + assertArrayEquals(args, args3); + } + } + + @Test + public void testLongSpreads() throws Throwable { + if (!startTest("testLongSpreads")) return; + MethodHandle id = identity(long[].class); + final int MAX = (MAX_ARITY - 2) / 2; // 253/2+1 would cause parameter overflow with 'this' added + for (int nargs = 0; nargs <= MAX; nargs++) { + if (nargs > 30 && nargs < MAX-20) nargs += 10; + long[] args = new long[nargs]; + for (int j = 0; j < args.length; j++) args[j] = (long)(j + 11); + //System.out.println("testLongSpreads "+Arrays.toString(args)); + long[] args1 = (long[]) id.invokeExact(args); + assertArrayEquals(args, args1); + MethodHandle coll = id.asCollector(long[].class, nargs); + long[] args2 = args; + switch (nargs) { + case 0: args2 = (long[]) coll.invokeExact(); break; + case 1: args2 = (long[]) coll.invokeExact(args[0]); break; + case 2: args2 = (long[]) coll.invokeExact(args[0], args[1]); break; + case 3: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2]); break; + case 4: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break; + case 5: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break; + } + assertArrayEquals(args, args2); + MethodHandle mh = coll.asSpreader(long[].class, nargs); + long[] args3 = (long[]) mh.invokeExact(args); + assertArrayEquals(args, args3); + } + } + + @Test + public void testIntCollects() throws Throwable { + if (!startTest("testIntCollects")) return; + for (MethodHandle lister : INT_LISTERS) { + int outputs = lister.type().parameterCount(); + for (int collects = 0; collects <= Math.min(outputs, INT_COLLECTORS.length-1); collects++) { + int inputs = outputs - 1 + collects; + if (inputs < 0) continue; + for (int pos = 0; pos + collects <= inputs; pos++) { + MethodHandle collector = INT_COLLECTORS[collects]; + int[] args = new int[inputs]; + int ap = 0, arg = 31; + for (int i = 0; i < pos; i++) + args[ap++] = arg++ + 0; + for (int i = 0; i < collects; i++) + args[ap++] = arg++ + 10; + while (ap < args.length) + args[ap++] = arg++ + 20; + // calculate piecemeal: + //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args)); + int[] collargs = Arrays.copyOfRange(args, pos, pos+collects); + int coll = (int) collector.asSpreader(int[].class, collargs.length).invokeExact(collargs); + int[] listargs = Arrays.copyOfRange(args, 0, outputs); + System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1)); + listargs[pos] = coll; + //System.out.println(" coll="+coll+" listargs="+Arrays.toString(listargs)); + Object expect = lister.asSpreader(int[].class, listargs.length).invokeExact(listargs); + //System.out.println(" expect="+expect); + + // now use the combined MH, and test the output: + MethodHandle mh = collectArguments(lister, pos, INT_COLLECTORS[collects]); + if (mh == null) continue; // no infix collection, yet + assert(mh.type().parameterCount() == inputs); + Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args); + assertEquals(expect, observe); + } + } + } + } + + private static MethodHandle collectArguments(MethodHandle lister, int pos, MethodHandle collector) { + int collects = collector.type().parameterCount(); + int outputs = lister.type().parameterCount(); + if (pos == outputs - 1) + return MethodHandles.filterArguments(lister, pos, + collector.asSpreader(int[].class, collects)) + .asCollector(int[].class, collects); + //return MethodHandles.collectArguments(lister, pos, collector); //no such animal + return null; + } + + private static final Class[] RETURN_TYPES = { + Object.class, String.class, Integer.class, + int.class, long.class, + boolean.class, byte.class, char.class, short.class, + float.class, double.class, + void.class, + }; + + @Test + public void testReturns() throws Throwable { + if (!startTest("testReturns")) return; + // fault injection: + int faultCount = 0; // total of 1296 tests + faultCount = Integer.getInteger("testReturns.faultCount", 0); + for (Class ret : RETURN_TYPES) { + // make a complicated identity function and pass something through it + System.out.println(ret.getSimpleName()); + Class vret = (ret == void.class) ? Void.class : ret; + MethodHandle id = // (vret)->ret + identity(vret).asType(methodType(ret, vret)); + final int LENGTH = 4; + int[] index = {0}; + Object vals = java.lang.reflect.Array.newInstance(vret, LENGTH); + MethodHandle indexGetter = //()->int + insertArguments(arrayElementGetter(index.getClass()), 0, index, 0); + MethodHandle valSelector = // (int)->vret + arrayElementGetter(vals.getClass()).bindTo(vals); + MethodHandle valGetter = // ()->vret + foldArguments(valSelector, indexGetter); + if (ret != void.class) { + for (int i = 0; i < LENGTH; i++) { + Object val = (i + 50); + if (ret == boolean.class) val = (i % 3 == 0); + if (ret == String.class) val = "#"+i; + if (ret == char.class) val = (char)('a'+i); + if (ret == byte.class) val = (byte)~i; + if (ret == short.class) val = (short)(1< "+val); + index[0] = i; + if (--faultCount == 0) index[0] ^= 1; + Object x = valGetter.invokeWithArguments(); + assertEquals(val, x); + // make a return-filter call: x = id(valGetter()) + if (--faultCount == 0) index[0] ^= 1; + x = filterReturnValue(valGetter, id).invokeWithArguments(); + assertEquals(val, x); + // make a filter call: x = id(*,valGetter(),*) + for (int len = 1; len <= 4; len++) { + for (int pos = 0; pos < len; pos++) { + MethodHandle proj = id; // lambda(..., vret x,...){x} + for (int j = 0; j < len; j++) { + if (j == pos) continue; + proj = dropArguments(proj, j, Object.class); + } + assert(proj.type().parameterCount() == len); + // proj: (Object*, pos: vret, Object*)->ret + assertEquals(vret, proj.type().parameterType(pos)); + MethodHandle vgFilter = dropArguments(valGetter, 0, Object.class); + if (--faultCount == 0) index[0] ^= 1; + x = filterArguments(proj, pos, vgFilter).invokeWithArguments(new Object[len]); + assertEquals(val, x); + } + } + // make a fold call: + for (int len = 0; len <= 4; len++) { + for (int fold = 0; fold <= len; fold++) { + MethodHandle proj = id; // lambda(ret x, ...){x} + if (ret == void.class) proj = constant(Object.class, null); + int arg0 = (ret == void.class ? 0 : 1); + for (int j = 0; j < len; j++) { + proj = dropArguments(proj, arg0, Object.class); + } + assert(proj.type().parameterCount() == arg0 + len); + // proj: (Object*, pos: vret, Object*)->ret + if (arg0 != 0) assertEquals(vret, proj.type().parameterType(0)); + MethodHandle vgFilter = valGetter.asType(methodType(ret)); + for (int j = 0; j < fold; j++) { + vgFilter = dropArguments(vgFilter, j, Object.class); + } + x = foldArguments(proj, vgFilter).invokeWithArguments(new Object[len]); + if (--faultCount == 0) index[0] ^= 1; + assertEquals(val, x); + } + } + } + } + //System.out.println("faultCount="+faultCount); + } + + private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) { + MethodHandle res = mh1; + for (MethodHandle mh2 : mhs) + res = filterReturnValue(res, mh2); + return res; + } + private static void assertEqualFunction(MethodHandle x, MethodHandle y) throws Throwable { + assertEquals(x.type(), y.type()); //?? + MethodType t = x.type(); + if (t.parameterCount() == 0) { + assertEqualFunctionAt(null, x, y); + return; + } + Class ptype = t.parameterType(0); + if (ptype == long.class || ptype == Long.class) { + for (long i = -10; i <= 10; i++) { + assertEqualFunctionAt(i, x, y); + } + } else { + for (int i = -10; i <= 10; i++) { + assertEqualFunctionAt(i, x, y); + } + } + } + private static void assertEqualFunctionAt(Object v, MethodHandle x, MethodHandle y) throws Throwable { + Object[] args = new Object[x.type().parameterCount()]; + Arrays.fill(args, v); + Object xval = invokeWithCatch(x, args); + Object yval = invokeWithCatch(y, args); + String msg = "ok"; + if (!Objects.equals(xval, yval)) { + msg = ("applying "+x+" & "+y+" to "+v); + } + assertEquals(msg, xval, yval); + } + private static Object invokeWithCatch(MethodHandle mh, Object... args) throws Throwable { + try { + return mh.invokeWithArguments(args); + } catch (Throwable ex) { + System.out.println("threw: "+mh+Arrays.asList(args)); + ex.printStackTrace(); + return ex; + } + } + + private static final Lookup LOOKUP = lookup(); + private static MethodHandle findStatic(String name, + Class rtype, + Class... ptypes) { + try { + return LOOKUP.findStatic(LOOKUP.lookupClass(), name, methodType(rtype, ptypes)); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + } + private static MethodHandle findStatic(String name, + Class rtype, + List ptypes) { + return findStatic(name, rtype, ptypes.toArray(new Class[ptypes.size()])); + } + static int getProperty(String name, int dflt) { + String qual = LOOKUP.lookupClass().getName(); + String prop = System.getProperty(qual+"."+name); + if (prop == null) prop = System.getProperty(name); + if (prop == null) return dflt; + return Integer.parseInt(prop); + } + + private static int opI(int... xs) { + stress(); + int base = 100; + int z = 0; + for (int x : xs) { + z = (z * base) + (x % base); + } + verbose("opI", xs.length, xs, z); + return z; + } + private static int opI2(int x, int y) { return opI(x, y); } // x*100 + y%100 + private static int opI3(int x, int y, int z) { return opI(x, y, z); } + private static int opI4(int w, int x, int y, int z) { return opI(w, x, y, z); } + private static int opI(int x) { return opI2(x, 37); } + private static Object opI_L(int x) { return (Object) opI(x); } + private static long opJ3(long x, long y, long z) { return (long) opI3((int)x, (int)y, (int)z); } + private static long opJ2(long x, long y) { return (long) opI2((int)x, (int)y); } + private static long opJ(long x) { return (long) opI((int)x); } + private static Object opL2(Object x, Object y) { return (Object) opI2((int)x, (int)y); } + private static Object opL(Object x) { return (Object) opI((int)x); } + private static int opL2_I(Object x, Object y) { return (int) opI2((int)x, (int)y); } + private static int opL_I(Object x) { return (int) opI((int)x); } + private static long opL_J(Object x) { return (long) opI((int)x); } + private static final MethodHandle opI, opI2, opI3, opI4, opI_L, opJ, opJ2, opJ3, opL2, opL, opL2_I, opL_I, opL_J; + static { + opI4 = findStatic("opI4", int.class, int.class, int.class, int.class, int.class); + opI3 = findStatic("opI3", int.class, int.class, int.class, int.class); + opI2 = findStatic("opI2", int.class, int.class, int.class); + opI = findStatic("opI", int.class, int.class); + opI_L = findStatic("opI_L", Object.class, int.class); + opJ = findStatic("opJ", long.class, long.class); + opJ2 = findStatic("opJ2", long.class, long.class, long.class); + opJ3 = findStatic("opJ3", long.class, long.class, long.class, long.class); + opL2 = findStatic("opL2", Object.class, Object.class, Object.class); + opL = findStatic("opL", Object.class, Object.class); + opL2_I = findStatic("opL2_I", int.class, Object.class, Object.class); + opL_I = findStatic("opL_I", int.class, Object.class); + opL_J = findStatic("opL_J", long.class, Object.class); + } + private static final MethodHandle[] INT_COLLECTORS = { + constant(int.class, 42), opI, opI2, opI3, opI4 + }; + private static final MethodHandle[] LONG_COLLECTORS = { + constant(long.class, 42), opJ, opJ2, opJ3 + }; + + private static int addI(int x, int y) { stress(); return x+y; } + private static Object addL(Object x, Object y) { return addI((int)x, (int)y); } + private static final MethodHandle addI, addL; + static { + addI = findStatic("addI", int.class, int.class, int.class); + addL = findStatic("addL", Object.class, Object.class, Object.class); + } + + private static Object list8ints(int a, int b, int c, int d, int e, int f, int g, int h) { + return Arrays.asList(a, b, c, d, e, f, g, h); + } + private static Object list8longs(long a, long b, long c, long d, long e, long f, long g, long h) { + return Arrays.asList(a, b, c, d, e, f, g, h); + } + private static final MethodHandle list8ints = findStatic("list8ints", Object.class, + Collections.nCopies(8, int.class)); + private static final MethodHandle list8longs = findStatic("list8longs", Object.class, + Collections.nCopies(8, long.class)); + private static final MethodHandle[] INT_LISTERS, LONG_LISTERS; + static { + int listerCount = list8ints.type().parameterCount() + 1; + INT_LISTERS = new MethodHandle[listerCount]; + LONG_LISTERS = new MethodHandle[listerCount]; + MethodHandle lister = list8ints; + MethodHandle llister = list8longs; + for (int i = listerCount - 1; ; i--) { + INT_LISTERS[i] = lister; + LONG_LISTERS[i] = llister; + if (i == 0) break; + lister = insertArguments(lister, i-1, (int)0); + llister = insertArguments(llister, i-1, (long)0); + } + } + + private static Object convI_L(int x) { stress(); return (Object) x; } + private static int convL_I(Object x) { stress(); return (int) x; } + private static Object convJ_L(long x) { stress(); return (Object) x; } + private static long convL_J(Object x) { stress(); return (long) x; } + private static int convJ_I(long x) { stress(); return (int) x; } + private static long convI_J(int x) { stress(); return (long) x; } + private static final MethodHandle convI_L, convL_I, convJ_L, convL_J, convJ_I, convI_J; + static { + convI_L = findStatic("convI_L", Object.class, int.class); + convL_I = findStatic("convL_I", int.class, Object.class); + convJ_L = findStatic("convJ_L", Object.class, long.class); + convL_J = findStatic("convL_J", long.class, Object.class); + convJ_I = findStatic("convJ_I", int.class, long.class); + convI_J = findStatic("convI_J", long.class, int.class); + } + + // stress modes: + private static final int REPEAT = getProperty("REPEAT", 0); + private static final int STRESS = getProperty("STRESS", 0); + private static /*v*/ int STRESS_COUNT; + private static final Object[] SINK = new Object[4]; + private static void stress() { + if (STRESS <= 0) return; + int count = STRESS + (STRESS_COUNT++ & 0x1); // non-constant value + for (int i = 0; i < count; i++) { + SINK[i % SINK.length] = new Object[STRESS + i % (SINK.length + 1)]; + } + } + + // verbosity: + private static final int VERBOSITY = getProperty("VERBOSITY", 0) + (REPEAT == 0 ? 0 : -1); + private static void verbose(Object a, Object b, Object c, Object d) { + if (VERBOSITY <= 0) return; + verbose(1, a, b, c, d); + } + private static void verbose(Object a, Object b, Object c) { + if (VERBOSITY <= 0) return; + verbose(1, a, b, c); + } + private static void verbose(int level, Object a, Object... bcd) { + if (level > VERBOSITY) return; + String m = a.toString(); + if (bcd != null && bcd.length > 0) { + List l = new ArrayList<>(bcd.length); + for (Object x : bcd) { + if (x instanceof Object[]) x = Arrays.asList((Object[])x); + if (x instanceof int[]) x = Arrays.toString((int[])x); + if (x instanceof long[]) x = Arrays.toString((long[])x); + l.add(x); + } + m = m+Arrays.asList(bcd); + } + System.out.println(m); + } + String testOnly; + String testOnlyTests; + private boolean startTest(String name) { + if (testOnly != null && !testOnly.contains(name)) + return false; + verbose(0, "["+name+"]"); + testOnlyTests = (testOnlyTests == null) ? name : testOnlyTests+" "+name; + return true; + } + +} diff --git a/jdk/test/sun/invoke/util/ValueConversionsTest.java b/jdk/test/sun/invoke/util/ValueConversionsTest.java new file mode 100644 index 00000000000..6b1253e77b2 --- /dev/null +++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2009, 2011, 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 test.sun.invoke.util; + +import sun.invoke.util.ValueConversions; +import sun.invoke.util.Wrapper; +import java.lang.invoke.MethodType; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +/* @test + * @summary unit tests for value-type conversion utilities + * @ignore This test requires a special compilation environment to access sun.inovke.util. Run by hand. + * @run junit/othervm test.sun.invoke.util.ValueConversionsTest + * @run junit/othervm + * -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.START_ARITY=250 + * test.sun.invoke.util.ValueConversionsTest + */ + +// This might take a while and burn lots of metadata: +// @run junit/othervm -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.EXHAUSTIVE=true test.sun.invoke.util.ValueConversionsTest + +/** + * + * @author jrose + */ +public class ValueConversionsTest { + private static final Class CLASS = ValueConversionsTest.class; + private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40); + private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0); + private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE"); + + @Test + public void testUnbox() throws Throwable { + testUnbox(false); + } + + @Test + public void testUnboxCast() throws Throwable { + testUnbox(true); + } + + private void testUnbox(boolean doCast) throws Throwable { + //System.out.println("unbox"); + for (Wrapper dst : Wrapper.values()) { + //System.out.println(dst); + for (Wrapper src : Wrapper.values()) { + testUnbox(doCast, dst, src); + } + } + } + + private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable { + boolean expectThrow = !doCast && !dst.isConvertibleFrom(src); + if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT) return; // must have prims + if (dst == Wrapper.OBJECT) + expectThrow = false; // everything (even VOID==null here) converts to OBJECT + try { + for (int n = -5; n < 10; n++) { + Object box = src.wrap(n); + switch (src) { + case VOID: assertEquals(box, null); break; + case OBJECT: box = box.toString(); break; + case SHORT: assertEquals(box.getClass(), Short.class); break; + default: assertEquals(box.getClass(), src.wrapperType()); break; + } + MethodHandle unboxer; + if (doCast) + unboxer = ValueConversions.unboxCast(dst.primitiveType()); + else + unboxer = ValueConversions.unbox(dst.primitiveType()); + Object expResult = (box == null) ? dst.zero() : dst.wrap(box); + Object result = null; + switch (dst) { + case INT: result = (int) unboxer.invokeExact(box); break; + case LONG: result = (long) unboxer.invokeExact(box); break; + case FLOAT: result = (float) unboxer.invokeExact(box); break; + case DOUBLE: result = (double) unboxer.invokeExact(box); break; + case CHAR: result = (char) unboxer.invokeExact(box); break; + case BYTE: result = (byte) unboxer.invokeExact(box); break; + case SHORT: result = (short) unboxer.invokeExact(box); break; + case OBJECT: result = (Object) unboxer.invokeExact(box); break; + case BOOLEAN: result = (boolean) unboxer.invokeExact(box); break; + case VOID: result = null; unboxer.invokeExact(box); break; + } + if (expectThrow) { + expResult = "(need an exception)"; + } + assertEquals("(doCast,expectThrow,dst,src,n,box)="+Arrays.asList(doCast,expectThrow,dst,src,n,box), + expResult, result); + } + } catch (RuntimeException ex) { + if (expectThrow) return; + System.out.println("Unexpected throw for (doCast,expectThrow,dst,src)="+Arrays.asList(doCast,expectThrow,dst,src)); + throw ex; + } + } + + @Test + public void testUnboxRaw() throws Throwable { + //System.out.println("unboxRaw"); + for (Wrapper w : Wrapper.values()) { + if (w == Wrapper.OBJECT) continue; // skip this; no raw form + //System.out.println(w); + for (int n = -5; n < 10; n++) { + Object box = w.wrap(n); + long expResult = w.unwrapRaw(box); + Object box2 = w.wrapRaw(expResult); + assertEquals(box, box2); + MethodHandle unboxer = ValueConversions.unboxRaw(w.primitiveType()); + long result = -1; + switch (w) { + case INT: result = (int) unboxer.invokeExact(box); break; + case LONG: result = (long) unboxer.invokeExact(box); break; + case FLOAT: result = (int) unboxer.invokeExact(box); break; + case DOUBLE: result = (long) unboxer.invokeExact(box); break; + case CHAR: result = (int) unboxer.invokeExact(box); break; + case BYTE: result = (int) unboxer.invokeExact(box); break; + case SHORT: result = (int) unboxer.invokeExact(box); break; + case BOOLEAN: result = (int) unboxer.invokeExact(box); break; + case VOID: result = (int) unboxer.invokeExact(box); break; + } + assertEquals("(w,n,box)="+Arrays.asList(w,n,box), + expResult, result); + } + } + } + + @Test + public void testBox() throws Throwable { + //System.out.println("box"); + for (Wrapper w : Wrapper.values()) { + if (w == Wrapper.VOID) continue; // skip this; no unboxed form + //System.out.println(w); + for (int n = -5; n < 10; n++) { + Object box = w.wrap(n); + MethodHandle boxer = ValueConversions.box(w.primitiveType()); + Object expResult = box; + Object result = null; + switch (w) { + case INT: result = boxer.invokeExact((int)n); break; + case LONG: result = boxer.invokeExact((long)n); break; + case FLOAT: result = boxer.invokeExact((float)n); break; + case DOUBLE: result = boxer.invokeExact((double)n); break; + case CHAR: result = boxer.invokeExact((char)n); break; + case BYTE: result = boxer.invokeExact((byte)n); break; + case SHORT: result = boxer.invokeExact((short)n); break; + case OBJECT: result = boxer.invokeExact((Object)n); break; + case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break; + } + assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box), + expResult, result); + } + } + } + + @Test + public void testBoxRaw() throws Throwable { + //System.out.println("boxRaw"); + for (Wrapper w : Wrapper.values()) { + if (w == Wrapper.VOID) continue; // skip this; no unboxed form + if (w == Wrapper.OBJECT) continue; // skip this; no raw form + //System.out.println(w); + for (int n = -5; n < 10; n++) { + Object box = w.wrap(n); + long raw = w.unwrapRaw(box); + Object expResult = box; + MethodHandle boxer = ValueConversions.boxRaw(w.primitiveType()); + Object result = null; + switch (w) { + case INT: result = boxer.invokeExact((int)raw); break; + case LONG: result = boxer.invokeExact(raw); break; + case FLOAT: result = boxer.invokeExact((int)raw); break; + case DOUBLE: result = boxer.invokeExact(raw); break; + case CHAR: result = boxer.invokeExact((int)raw); break; + case BYTE: result = boxer.invokeExact((int)raw); break; + case SHORT: result = boxer.invokeExact((int)raw); break; + case BOOLEAN: result = boxer.invokeExact((int)raw); break; + } + assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box), + expResult, result); + } + } + } + + @Test + public void testReboxRaw() throws Throwable { + //System.out.println("reboxRaw"); + for (Wrapper w : Wrapper.values()) { + Wrapper pw = Wrapper.forPrimitiveType(w.rawPrimitiveType()); + if (w == Wrapper.VOID) continue; // skip this; no unboxed form + if (w == Wrapper.OBJECT) continue; // skip this; no raw form + //System.out.println(w); + for (int n = -5; n < 10; n++) { + Object box = w.wrap(n); + Object raw = pw.wrap(w.unwrapRaw(box)); + Object expResult = box; + MethodHandle boxer = ValueConversions.rebox(w.primitiveType()); + Object result = null; + switch (w) { + case INT: result = boxer.invokeExact(raw); break; + case LONG: result = boxer.invokeExact(raw); break; + case FLOAT: result = boxer.invokeExact(raw); break; + case DOUBLE: result = boxer.invokeExact(raw); break; + case CHAR: result = boxer.invokeExact(raw); break; + case BYTE: result = boxer.invokeExact(raw); break; + case SHORT: result = boxer.invokeExact(raw); break; + case BOOLEAN: result = boxer.invokeExact(raw); break; + } + assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box), + expResult, result); + } + } + } + + @Test + public void testCast() throws Throwable { + //System.out.println("cast"); + Class[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class }; + Object[] objects = { new Object(), Boolean.FALSE, "hello", (Long)12L, (Integer)6 }; + for (Class dst : types) { + MethodHandle caster = ValueConversions.cast(dst); + assertEquals(caster.type(), ValueConversions.identity().type()); + for (Object obj : objects) { + Class src = obj.getClass(); + boolean canCast; + if (dst.isInterface()) { + canCast = true; + } else { + canCast = dst.isAssignableFrom(src); + assertEquals(canCast, dst.isInstance(obj)); + } + //System.out.println("obj="+obj+" <: dst="+dst); + try { + Object result = caster.invokeExact(obj); + if (canCast) + assertEquals(obj, result); + else + assertEquals("cast should not have succeeded", dst, obj); + } catch (ClassCastException ex) { + if (canCast) + throw ex; + } + } + } + } + + @Test + public void testIdentity() throws Throwable { + //System.out.println("identity"); + MethodHandle id = ValueConversions.identity(); + Object expResult = "foo"; + Object result = id.invokeExact(expResult); + // compiler bug: ValueConversions.identity().invokeExact("bar"); + assertEquals(expResult, result); + } + + @Test + public void testVarargsArray() throws Throwable { + //System.out.println("varargsArray"); + final int MIN = START_ARITY; + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) { + MethodHandle target = ValueConversions.varargsArray(nargs); + Object[] args = new Object[nargs]; + for (int i = 0; i < nargs; i++) + args[i] = "#"+i; + Object res = target.invokeWithArguments(args); + assertArrayEquals(args, (Object[])res); + } + } + + @Test + public void testVarargsReferenceArray() throws Throwable { + //System.out.println("varargsReferenceArray"); + testTypedVarargsArray(Object[].class); + testTypedVarargsArray(String[].class); + testTypedVarargsArray(Number[].class); + } + + @Test + public void testVarargsPrimitiveArray() throws Throwable { + //System.out.println("varargsPrimitiveArray"); + testTypedVarargsArray(int[].class); + testTypedVarargsArray(long[].class); + testTypedVarargsArray(byte[].class); + testTypedVarargsArray(boolean[].class); + testTypedVarargsArray(short[].class); + testTypedVarargsArray(char[].class); + testTypedVarargsArray(float[].class); + testTypedVarargsArray(double[].class); + } + + private static int nextArgCount(int nargs, int density, int MAX) { + if (EXHAUSTIVE) return nargs + 1; + if (nargs >= MAX) return Integer.MAX_VALUE; + int BOT = 20, TOP = MAX-5; + if (density < 10) { BOT = 10; MAX = TOP-2; } + if (nargs <= BOT || nargs >= TOP) { + ++nargs; + } else { + int bump = Math.max(1, 100 / density); + nargs += bump; + if (nargs > TOP) nargs = TOP; + } + return nargs; + } + + private void testTypedVarargsArray(Class arrayType) throws Throwable { + System.out.println(arrayType.getSimpleName()); + Class elemType = arrayType.getComponentType(); + int MIN = START_ARITY; + int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + int density = 3; + if (elemType == int.class || elemType == long.class) density = 7; + if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; } + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) { + Object[] args = makeTestArray(elemType, nargs); + MethodHandle varargsArray = ValueConversions.varargsArray(arrayType, nargs); + MethodType vaType = varargsArray.type(); + assertEquals(arrayType, vaType.returnType()); + if (nargs != 0) { + assertEquals(elemType, vaType.parameterType(0)); + assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1)); + } + assertEquals(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType)), + vaType); + Object res = varargsArray.invokeWithArguments(args); + String resString = toArrayString(res); + assertEquals(Arrays.toString(args), resString); + + MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs); + MethodType stype = spreader.type(); + assert(stype == MethodType.methodType(arrayType, arrayType)); + if (nargs <= 5) { + // invoke target as a spreader also: + Object res2 = spreader.invokeWithArguments((Object)res); + String res2String = toArrayString(res2); + assertEquals(Arrays.toString(args), res2String); + // invoke the spreader on a generic Object[] array; check for error + try { + Object res3 = spreader.invokeWithArguments((Object)args); + String res3String = toArrayString(res3); + assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); + assertEquals(Arrays.toString(args), res3String); + } catch (ClassCastException ex) { + assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); + } + } + if (nargs == 0) { + // invoke spreader on null arglist + Object res3 = spreader.invokeWithArguments((Object)null); + String res3String = toArrayString(res3); + assertEquals(Arrays.toString(args), res3String); + } + } + } + + private static Object[] makeTestArray(Class elemType, int len) { + Wrapper elem = null; + if (elemType.isPrimitive()) + elem = Wrapper.forPrimitiveType(elemType); + else if (Wrapper.isWrapperType(elemType)) + elem = Wrapper.forWrapperType(elemType); + Object[] args = new Object[len]; + for (int i = 0; i < len; i++) { + Object arg = i * 100; + if (elem == null) { + if (elemType == String.class) + arg = "#"+arg; + arg = elemType.cast(arg); // just to make sure + } else { + switch (elem) { + case BOOLEAN: arg = (i % 3 == 0); break; + case CHAR: arg = 'a' + i; break; + case LONG: arg = (long)i * 1000_000_000; break; + case FLOAT: arg = (float)i / 100; break; + case DOUBLE: arg = (double)i / 1000_000; break; + } + arg = elem.cast(arg, elemType); + } + args[i] = arg; + } + //System.out.println(elemType.getName()+Arrays.toString(args)); + return args; + } + + private static String toArrayString(Object a) { + if (a == null) return "null"; + Class elemType = a.getClass().getComponentType(); + if (elemType == null) return a.toString(); + if (elemType.isPrimitive()) { + switch (Wrapper.forPrimitiveType(elemType)) { + case INT: return Arrays.toString((int[])a); + case BYTE: return Arrays.toString((byte[])a); + case BOOLEAN: return Arrays.toString((boolean[])a); + case SHORT: return Arrays.toString((short[])a); + case CHAR: return Arrays.toString((char[])a); + case FLOAT: return Arrays.toString((float[])a); + case LONG: return Arrays.toString((long[])a); + case DOUBLE: return Arrays.toString((double[])a); + } + } + return Arrays.toString((Object[])a); + } + + @Test + public void testVarargsList() throws Throwable { + //System.out.println("varargsList"); + final int MIN = START_ARITY; + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 7, MAX)) { + MethodHandle target = ValueConversions.varargsList(nargs); + Object[] args = new Object[nargs]; + for (int i = 0; i < nargs; i++) + args[i] = "#"+i; + Object res = target.invokeWithArguments(args); + assertEquals(Arrays.asList(args), res); + } + } +} From 14216b3f51dd3fbebc37652f93acaec0bf80ac99 Mon Sep 17 00:00:00 2001 From: Erik Trimble Date: Thu, 12 May 2011 21:42:52 -0700 Subject: [PATCH 52/65] Added tag hs21-b12 for changeset 19acba38f929 --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index f7a4fe8e7f8..8b20d73be2a 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -171,3 +171,4 @@ d283b82966712b353fa307845a1316da42a355f4 hs21-b10 5d07913abd59261c77f24cc04a759cb75d804099 jdk7-b141 3aea9e9feb073f5500e031be6186666bcae89aa2 hs21-b11 9ad1548c6b63d596c411afc35147ffd5254426d9 jdk7-b142 +9ad1548c6b63d596c411afc35147ffd5254426d9 hs21-b12 From 643341dea0cb2156836d82627ce2ae22f4c7b222 Mon Sep 17 00:00:00 2001 From: Erik Trimble Date: Thu, 12 May 2011 22:05:08 -0700 Subject: [PATCH 53/65] 7040780: Bump the HS21 build number to 13 Update the HS21 build number to 13 Reviewed-by: jcoomes --- hotspot/make/hotspot_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/make/hotspot_version b/hotspot/make/hotspot_version index a8dda3ec3ba..452ad051644 100644 --- a/hotspot/make/hotspot_version +++ b/hotspot/make/hotspot_version @@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2011 HS_MAJOR_VER=21 HS_MINOR_VER=0 -HS_BUILD_NUMBER=12 +HS_BUILD_NUMBER=13 JDK_MAJOR_VER=1 JDK_MINOR_VER=7 From 2804648f956d058cba4b0da35a5163e61cc47e8f Mon Sep 17 00:00:00 2001 From: Frederic Parain Date: Fri, 13 May 2011 13:20:16 +0200 Subject: [PATCH 54/65] 7031754: javax.management docs need to be updated to replace Java SE 6 occurrences Remove references to a specific version of the Java Platform Reviewed-by: mchung, kamg --- jdk/src/share/classes/javax/management/loading/package.html | 4 ++-- .../share/classes/javax/management/modelmbean/package.html | 4 ++-- jdk/src/share/classes/javax/management/monitor/package.html | 4 ++-- jdk/src/share/classes/javax/management/openmbean/package.html | 4 ++-- jdk/src/share/classes/javax/management/package.html | 4 ++-- jdk/src/share/classes/javax/management/relation/package.html | 4 ++-- jdk/src/share/classes/javax/management/remote/package.html | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/jdk/src/share/classes/javax/management/loading/package.html b/jdk/src/share/classes/javax/management/loading/package.html index 7b7ca68a99f..1cfeef73bfa 100644 --- a/jdk/src/share/classes/javax/management/loading/package.html +++ b/jdk/src/share/classes/javax/management/loading/package.html @@ -2,7 +2,7 @@ javax.management.loading package