提交 644b712e 编写于 作者: L lana

Merge

...@@ -91,3 +91,5 @@ b53f226b1d91473ac54184afa827be07b87e0319 jdk7-b112 ...@@ -91,3 +91,5 @@ b53f226b1d91473ac54184afa827be07b87e0319 jdk7-b112
e250cef36ea05e627e7e6f7d75e5e19f529e2ba3 jdk7-b114 e250cef36ea05e627e7e6f7d75e5e19f529e2ba3 jdk7-b114
449bad8d67b5808ecf0f927683acc0a5940f8c85 jdk7-b115 449bad8d67b5808ecf0f927683acc0a5940f8c85 jdk7-b115
1657ed4e1d86c8aa2028ab5a41f9da1ac4a369f8 jdk7-b116 1657ed4e1d86c8aa2028ab5a41f9da1ac4a369f8 jdk7-b116
3e6726bbf80a4254ecd01051c8ed77ee19325e46 jdk7-b117
b357910aa04aead2a16b6d6ff395a8df4b51d1dd jdk7-b118
...@@ -53,7 +53,7 @@ FILES_export = \ ...@@ -53,7 +53,7 @@ FILES_export = \
# #
# Extra cc/linker flags. # Extra cc/linker flags.
# #
LDLIBS += dsound.lib winmm.lib user32.lib LDLIBS += dsound.lib winmm.lib user32.lib ole32.lib
CPPFLAGS += \ CPPFLAGS += \
-DUSE_DAUDIO=TRUE \ -DUSE_DAUDIO=TRUE \
-I$(SHARE_SRC)/native/com/sun/media/sound \ -I$(SHARE_SRC)/native/com/sun/media/sound \
......
...@@ -21,4 +21,4 @@ ...@@ -21,4 +21,4 @@
# or visit www.oracle.com if you need additional information or have any # or visit www.oracle.com if you need additional information or have any
# questions. # questions.
# #
tzdata2010l tzdata2010o
...@@ -569,8 +569,8 @@ Rule HK 1953 only - Nov 1 3:30 0 - ...@@ -569,8 +569,8 @@ Rule HK 1953 only - Nov 1 3:30 0 -
Rule HK 1954 1964 - Mar Sun>=18 3:30 1:00 S Rule HK 1954 1964 - Mar Sun>=18 3:30 1:00 S
Rule HK 1954 only - Oct 31 3:30 0 - Rule HK 1954 only - Oct 31 3:30 0 -
Rule HK 1955 1964 - Nov Sun>=1 3:30 0 - Rule HK 1955 1964 - Nov Sun>=1 3:30 0 -
Rule HK 1965 1977 - Apr Sun>=16 3:30 1:00 S Rule HK 1965 1976 - Apr Sun>=16 3:30 1:00 S
Rule HK 1965 1977 - Oct Sun>=16 3:30 0 - Rule HK 1965 1976 - Oct Sun>=16 3:30 0 -
Rule HK 1973 only - Dec 30 3:30 1:00 S Rule HK 1973 only - Dec 30 3:30 1:00 S
Rule HK 1979 only - May Sun>=8 3:30 1:00 S Rule HK 1979 only - May Sun>=8 3:30 1:00 S
Rule HK 1979 only - Oct Sun>=16 3:30 0 - Rule HK 1979 only - Oct Sun>=16 3:30 0 -
......
...@@ -306,13 +306,26 @@ Zone Indian/Cocos 6:27:40 - LMT 1900 ...@@ -306,13 +306,26 @@ Zone Indian/Cocos 6:27:40 - LMT 1900
# http://www.timeanddate.com/news/time/fiji-dst-ends-march-2010.html # http://www.timeanddate.com/news/time/fiji-dst-ends-march-2010.html
# </a> # </a>
# From Alexander Krivenyshev (2010-10-24):
# According to Radio Fiji and Fiji Times online, Fiji will end DST 3
# weeks earlier than expected - on March 6, 2011, not March 27, 2011...
# Here is confirmation from Government of the Republic of the Fiji Islands,
# Ministry of Information (fiji.gov.fj) web site:
# <a href="http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=2608:daylight-savings&catid=71:press-releases&Itemid=155">
# http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=2608:daylight-savings&catid=71:press-releases&Itemid=155
# </a>
# or
# <a href="http://www.worldtimezone.com/dst_news/dst_news_fiji04.html">
# http://www.worldtimezone.com/dst_news/dst_news_fiji04.html
# </a>
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
Rule Fiji 1998 1999 - Nov Sun>=1 2:00 1:00 S Rule Fiji 1998 1999 - Nov Sun>=1 2:00 1:00 S
Rule Fiji 1999 2000 - Feb lastSun 3:00 0 - Rule Fiji 1999 2000 - Feb lastSun 3:00 0 -
Rule Fiji 2009 only - Nov 29 2:00 1:00 S Rule Fiji 2009 only - Nov 29 2:00 1:00 S
Rule Fiji 2010 only - Mar lastSun 3:00 0 - Rule Fiji 2010 only - Mar lastSun 3:00 0 -
Rule Fiji 2010 only - Oct 24 2:00 1:00 S Rule Fiji 2010 only - Oct 24 2:00 1:00 S
Rule Fiji 2011 only - Mar lastSun 3:00 0 - Rule Fiji 2011 only - Mar Sun>=1 3:00 0 -
# Zone NAME GMTOFF RULES FORMAT [UNTIL] # Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Pacific/Fiji 11:53:40 - LMT 1915 Oct 26 # Suva Zone Pacific/Fiji 11:53:40 - LMT 1915 Oct 26 # Suva
12:00 Fiji FJ%sT # Fiji Time 12:00 Fiji FJ%sT # Fiji Time
...@@ -509,11 +522,21 @@ Zone Pacific/Pago_Pago 12:37:12 - LMT 1879 Jul 5 ...@@ -509,11 +522,21 @@ Zone Pacific/Pago_Pago 12:37:12 - LMT 1879 Jul 5
# http://www.parliament.gov.ws/documents/acts/Daylight%20Saving%20Act%20%202009%20%28English%29%20-%20Final%207-7-091.pdf # http://www.parliament.gov.ws/documents/acts/Daylight%20Saving%20Act%20%202009%20%28English%29%20-%20Final%207-7-091.pdf
# </a> # </a>
# From Raymond Hughes (2010-10-07):
# Please see
# <a href="http://www.mcil.gov.ws">
# http://www.mcil.gov.ws
# </a>,
# the Ministry of Commerce, Industry and Labour (sideframe) "Last Sunday
# September 2010 (26/09/10) - adjust clocks forward from 12:00 midnight
# to 01:00am and First Sunday April 2011 (03/04/11) - adjust clocks
# backwards from 1:00am to 12:00am"
Zone Pacific/Apia 12:33:04 - LMT 1879 Jul 5 Zone Pacific/Apia 12:33:04 - LMT 1879 Jul 5
-11:26:56 - LMT 1911 -11:26:56 - LMT 1911
-11:30 - SAMT 1950 # Samoa Time -11:30 - SAMT 1950 # Samoa Time
-11:00 - WST 2010 Sep 26 -11:00 - WST 2010 Sep 26
-11:00 1:00 WSDT 2011 Apr 3 -11:00 1:00 WSDT 2011 Apr 3 1:00
-11:00 - WST -11:00 - WST
# Solomon Is # Solomon Is
......
...@@ -63,7 +63,7 @@ AQ -6448-06406 Antarctica/Palmer Palmer Station, Anvers Island ...@@ -63,7 +63,7 @@ AQ -6448-06406 Antarctica/Palmer Palmer Station, Anvers Island
AQ -6736+06253 Antarctica/Mawson Mawson Station, Holme Bay AQ -6736+06253 Antarctica/Mawson Mawson Station, Holme Bay
AQ -6835+07758 Antarctica/Davis Davis Station, Vestfold Hills AQ -6835+07758 Antarctica/Davis Davis Station, Vestfold Hills
AQ -6617+11031 Antarctica/Casey Casey Station, Bailey Peninsula AQ -6617+11031 Antarctica/Casey Casey Station, Bailey Peninsula
AQ -7824+10654 Antarctica/Vostok Vostok Station, S Magnetic Pole AQ -7824+10654 Antarctica/Vostok Vostok Station, Lake Vostok
AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie
AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I
AQ -5430+15857 Antarctica/Macquarie Macquarie Island Station, Macquarie Island AQ -5430+15857 Antarctica/Macquarie Macquarie Island Station, Macquarie Island
......
/* /*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
package java.awt; package java.awt;
import java.beans.ConstructorProperties;
/** /**
* The <code>BasicStroke</code> class defines a basic set of rendering * The <code>BasicStroke</code> class defines a basic set of rendering
* attributes for the outlines of graphics primitives, which are rendered * attributes for the outlines of graphics primitives, which are rendered
...@@ -183,6 +185,7 @@ public class BasicStroke implements Stroke { ...@@ -183,6 +185,7 @@ public class BasicStroke implements Stroke {
* <code>dash</code> is zero * <code>dash</code> is zero
* @throws IllegalArgumentException if dash lengths are all zero. * @throws IllegalArgumentException if dash lengths are all zero.
*/ */
@ConstructorProperties({ "lineWidth", "endCap", "lineJoin", "miterLimit", "dashArray", "dashPhase" })
public BasicStroke(float width, int cap, int join, float miterlimit, public BasicStroke(float width, int cap, int join, float miterlimit,
float dash[], float dash_phase) { float dash[], float dash_phase) {
if (width < 0.0f) { if (width < 0.0f) {
......
/* /*
* Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,6 +29,7 @@ import java.awt.geom.Point2D; ...@@ -29,6 +29,7 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel; import java.awt.image.ColorModel;
import java.beans.ConstructorProperties;
/** /**
* The <code>GradientPaint</code> class provides a way to fill * The <code>GradientPaint</code> class provides a way to fill
...@@ -166,6 +167,7 @@ public class GradientPaint implements Paint { ...@@ -166,6 +167,7 @@ public class GradientPaint implements Paint {
* @throws NullPointerException if either one of colors or points * @throws NullPointerException if either one of colors or points
* is null * is null
*/ */
@ConstructorProperties({ "point1", "color1", "point2", "color2", "cyclic" })
public GradientPaint(Point2D pt1, public GradientPaint(Point2D pt1,
Color color1, Color color1,
Point2D pt2, Point2D pt2,
......
...@@ -126,7 +126,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -126,7 +126,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
/** /**
* Place the component centered along the edge of its display area * Place the component centered along the edge of its display area
* associated with the start of a page for the current * associated with the start of a page for the current
* <code>ComponentOrienation</code>. Equal to NORTH for horizontal * {@code ComponentOrientation}. Equal to NORTH for horizontal
* orientations. * orientations.
*/ */
public static final int PAGE_START = 19; public static final int PAGE_START = 19;
...@@ -134,7 +134,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -134,7 +134,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
/** /**
* Place the component centered along the edge of its display area * Place the component centered along the edge of its display area
* associated with the end of a page for the current * associated with the end of a page for the current
* <code>ComponentOrienation</code>. Equal to SOUTH for horizontal * {@code ComponentOrientation}. Equal to SOUTH for horizontal
* orientations. * orientations.
*/ */
public static final int PAGE_END = 20; public static final int PAGE_END = 20;
...@@ -142,7 +142,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -142,7 +142,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
/** /**
* Place the component centered along the edge of its display area where * Place the component centered along the edge of its display area where
* lines of text would normally begin for the current * lines of text would normally begin for the current
* <code>ComponentOrienation</code>. Equal to WEST for horizontal, * {@code ComponentOrientation}. Equal to WEST for horizontal,
* left-to-right orientations and EAST for horizontal, right-to-left * left-to-right orientations and EAST for horizontal, right-to-left
* orientations. * orientations.
*/ */
...@@ -151,7 +151,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -151,7 +151,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
/** /**
* Place the component centered along the edge of its display area where * Place the component centered along the edge of its display area where
* lines of text would normally end for the current * lines of text would normally end for the current
* <code>ComponentOrienation</code>. Equal to EAST for horizontal, * {@code ComponentOrientation}. Equal to EAST for horizontal,
* left-to-right orientations and WEST for horizontal, right-to-left * left-to-right orientations and WEST for horizontal, right-to-left
* orientations. * orientations.
*/ */
...@@ -160,7 +160,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -160,7 +160,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
/** /**
* Place the component in the corner of its display area where * Place the component in the corner of its display area where
* the first line of text on a page would normally begin for the current * the first line of text on a page would normally begin for the current
* <code>ComponentOrienation</code>. Equal to NORTHWEST for horizontal, * {@code ComponentOrientation}. Equal to NORTHWEST for horizontal,
* left-to-right orientations and NORTHEAST for horizontal, right-to-left * left-to-right orientations and NORTHEAST for horizontal, right-to-left
* orientations. * orientations.
*/ */
...@@ -169,7 +169,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -169,7 +169,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
/** /**
* Place the component in the corner of its display area where * Place the component in the corner of its display area where
* the first line of text on a page would normally end for the current * the first line of text on a page would normally end for the current
* <code>ComponentOrienation</code>. Equal to NORTHEAST for horizontal, * {@code ComponentOrientation}. Equal to NORTHEAST for horizontal,
* left-to-right orientations and NORTHWEST for horizontal, right-to-left * left-to-right orientations and NORTHWEST for horizontal, right-to-left
* orientations. * orientations.
*/ */
...@@ -178,7 +178,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -178,7 +178,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
/** /**
* Place the component in the corner of its display area where * Place the component in the corner of its display area where
* the last line of text on a page would normally start for the current * the last line of text on a page would normally start for the current
* <code>ComponentOrienation</code>. Equal to SOUTHWEST for horizontal, * {@code ComponentOrientation}. Equal to SOUTHWEST for horizontal,
* left-to-right orientations and SOUTHEAST for horizontal, right-to-left * left-to-right orientations and SOUTHEAST for horizontal, right-to-left
* orientations. * orientations.
*/ */
...@@ -187,7 +187,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -187,7 +187,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
/** /**
* Place the component in the corner of its display area where * Place the component in the corner of its display area where
* the last line of text on a page would normally end for the current * the last line of text on a page would normally end for the current
* <code>ComponentOrienation</code>. Equal to SOUTHEAST for horizontal, * {@code ComponentOrientation}. Equal to SOUTHEAST for horizontal,
* left-to-right orientations and SOUTHWEST for horizontal, right-to-left * left-to-right orientations and SOUTHWEST for horizontal, right-to-left
* orientations. * orientations.
*/ */
...@@ -437,7 +437,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { ...@@ -437,7 +437,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable {
* <code>LINE_START</code>, <code>LINE_END</code>, * <code>LINE_START</code>, <code>LINE_END</code>,
* <code>FIRST_LINE_START</code>, <code>FIRST_LINE_END</code>, * <code>FIRST_LINE_START</code>, <code>FIRST_LINE_END</code>,
* <code>LAST_LINE_START</code> and <code>LAST_LINE_END</code>. The * <code>LAST_LINE_START</code> and <code>LAST_LINE_END</code>. The
* baseline relvative values are: * baseline relative values are:
* <code>BASELINE</code>, <code>BASELINE_LEADING</code>, * <code>BASELINE</code>, <code>BASELINE_LEADING</code>,
* <code>BASELINE_TRAILING</code>, * <code>BASELINE_TRAILING</code>,
* <code>ABOVE_BASELINE</code>, <code>ABOVE_BASELINE_LEADING</code>, * <code>ABOVE_BASELINE</code>, <code>ABOVE_BASELINE_LEADING</code>,
......
/* /*
* Copyright (c) 2006, 2008, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,10 +26,10 @@ ...@@ -26,10 +26,10 @@
package java.awt; package java.awt;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel; import java.awt.image.ColorModel;
import java.beans.ConstructorProperties;
/** /**
* The {@code LinearGradientPaint} class provides a way to fill * The {@code LinearGradientPaint} class provides a way to fill
...@@ -271,6 +271,7 @@ public final class LinearGradientPaint extends MultipleGradientPaint { ...@@ -271,6 +271,7 @@ public final class LinearGradientPaint extends MultipleGradientPaint {
* or a {@code fractions} value is less than 0.0 or greater than 1.0, * or a {@code fractions} value is less than 0.0 or greater than 1.0,
* or the {@code fractions} are not provided in strictly increasing order * or the {@code fractions} are not provided in strictly increasing order
*/ */
@ConstructorProperties({ "startPoint", "endPoint", "fractions", "colors", "cycleMethod", "colorSpace", "transform" })
public LinearGradientPaint(Point2D start, Point2D end, public LinearGradientPaint(Point2D start, Point2D end,
float[] fractions, Color[] colors, float[] fractions, Color[] colors,
CycleMethod cycleMethod, CycleMethod cycleMethod,
......
/* /*
* Copyright (c) 2006, 2008, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,6 +29,7 @@ import java.awt.geom.AffineTransform; ...@@ -29,6 +29,7 @@ import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel; import java.awt.image.ColorModel;
import java.beans.ConstructorProperties;
/** /**
* The {@code RadialGradientPaint} class provides a way to fill a shape with * The {@code RadialGradientPaint} class provides a way to fill a shape with
...@@ -428,6 +429,7 @@ public final class RadialGradientPaint extends MultipleGradientPaint { ...@@ -428,6 +429,7 @@ public final class RadialGradientPaint extends MultipleGradientPaint {
* or a {@code fractions} value is less than 0.0 or greater than 1.0, * or a {@code fractions} value is less than 0.0 or greater than 1.0,
* or the {@code fractions} are not provided in strictly increasing order * or the {@code fractions} are not provided in strictly increasing order
*/ */
@ConstructorProperties({ "centerPoint", "radius", "focusPoint", "fractions", "colors", "cycleMethod", "colorSpace", "transform" })
public RadialGradientPaint(Point2D center, public RadialGradientPaint(Point2D center,
float radius, float radius,
Point2D focus, Point2D focus,
......
...@@ -213,7 +213,8 @@ public class Scrollbar extends Component implements Adjustable, Accessible { ...@@ -213,7 +213,8 @@ public class Scrollbar extends Component implements Adjustable, Accessible {
* The size of the <code>Scrollbar</code>'s bubble. * The size of the <code>Scrollbar</code>'s bubble.
* When a scroll bar is used to select a range of values, * When a scroll bar is used to select a range of values,
* the visibleAmount represents the size of this range. * the visibleAmount represents the size of this range.
* This is visually indicated by the size of the bubble. * Depending on platform, this may be visually indicated
* by the size of the bubble.
* *
* @serial * @serial
* @see #getVisibleAmount * @see #getVisibleAmount
...@@ -637,6 +638,8 @@ public class Scrollbar extends Component implements Adjustable, Accessible { ...@@ -637,6 +638,8 @@ public class Scrollbar extends Component implements Adjustable, Accessible {
* bubble (also called a thumb or scroll box), usually gives a * bubble (also called a thumb or scroll box), usually gives a
* visual representation of the relationship of the visible * visual representation of the relationship of the visible
* amount to the range of the scroll bar. * amount to the range of the scroll bar.
* Note that depending on platform, the value of the visible amount property
* may not be visually indicated by the size of the bubble.
* <p> * <p>
* The scroll bar's bubble may not be displayed when it is not * The scroll bar's bubble may not be displayed when it is not
* moveable (e.g. when it takes up the entire length of the * moveable (e.g. when it takes up the entire length of the
...@@ -670,6 +673,8 @@ public class Scrollbar extends Component implements Adjustable, Accessible { ...@@ -670,6 +673,8 @@ public class Scrollbar extends Component implements Adjustable, Accessible {
* bubble (also called a thumb or scroll box), usually gives a * bubble (also called a thumb or scroll box), usually gives a
* visual representation of the relationship of the visible * visual representation of the relationship of the visible
* amount to the range of the scroll bar. * amount to the range of the scroll bar.
* Note that depending on platform, the value of the visible amount property
* may not be visually indicated by the size of the bubble.
* <p> * <p>
* The scroll bar's bubble may not be displayed when it is not * The scroll bar's bubble may not be displayed when it is not
* moveable (e.g. when it takes up the entire length of the * moveable (e.g. when it takes up the entire length of the
......
...@@ -1831,7 +1831,11 @@ public abstract class Toolkit { ...@@ -1831,7 +1831,11 @@ public abstract class Toolkit {
desktopProperties.put(name, newValue); desktopProperties.put(name, newValue);
} }
desktopPropsSupport.firePropertyChange(name, oldValue, newValue); // Don't fire change event if old and new values are null.
// It helps to avoid recursive resending of WM_THEMECHANGED
if (oldValue != null || newValue != null) {
desktopPropsSupport.firePropertyChange(name, oldValue, newValue);
}
} }
/** /**
......
/* /*
* Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
package java.awt.geom; package java.awt.geom;
import java.awt.Shape; import java.awt.Shape;
import java.beans.ConstructorProperties;
/** /**
* The <code>AffineTransform</code> class represents a 2D affine transform * The <code>AffineTransform</code> class represents a 2D affine transform
...@@ -508,6 +509,7 @@ public class AffineTransform implements Cloneable, java.io.Serializable { ...@@ -508,6 +509,7 @@ public class AffineTransform implements Cloneable, java.io.Serializable {
* @param m12 the Y coordinate translation element of the 3x3 matrix * @param m12 the Y coordinate translation element of the 3x3 matrix
* @since 1.2 * @since 1.2
*/ */
@ConstructorProperties({ "scaleX", "shearY", "shearX", "scaleY", "translateX", "translateY" })
public AffineTransform(float m00, float m10, public AffineTransform(float m00, float m10,
float m01, float m11, float m01, float m11,
float m02, float m12) { float m02, float m12) {
......
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.dyn;
import java.lang.annotation.*;
/**
* Annotation on InvokeDynamic method calls which requests the JVM to use a specific
* <a href="package-summary.html#bsm">bootstrap method</a>
* to link the call. This annotation is not retained as such in the class file,
* but is transformed into a constant-pool entry for the invokedynamic instruction which
* specifies the desired bootstrap method.
* <p>
* If only the <code>value</code> is given, it must name a subclass of {@link CallSite}
* with a constructor which accepts a class, string, and method type.
* If the <code>value</code> and <code>name</code> are both given, there must be
* a static method in the given class of the given name which accepts a class, string,
* and method type, and returns a reference coercible to {@link CallSite}.
* <p>
* This annotation can be placed either on the return type of a single {@link InvokeDynamic}
* call (see examples) or else it can be placed on an enclosing class or method, where it
* determines a default bootstrap method for any {@link InvokeDynamic} calls which are not
* specifically annotated with a bootstrap method.
* Every {@link InvokeDynamic} call must be given a bootstrap method.
* <p>
* Examples:
<blockquote><pre>
&#064;BootstrapMethod(value=MyLanguageRuntime.class, name="bootstrapDynamic")
String x = (String) InvokeDynamic.greet();
//BSM => MyLanguageRuntime.bootstrapDynamic(Here.class, "greet", methodType(String.class))
&#064;BootstrapMethod(MyCallSite.class)
void example() throws Throwable {
InvokeDynamic.greet();
//BSM => new MyCallSite(Here.class, "greet", methodType(void.class))
}
</pre></blockquote>
* <p>
*/
@Target({ElementType.TYPE_USE,
// For defaulting every indy site within a class or method; cf. @SuppressWarnings:
ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR
})
@Retention(RetentionPolicy.SOURCE)
public @interface BootstrapMethod {
/** The class containing the bootstrap method. */
Class<?> value();
/** The name of the bootstrap method.
* If this is the empty string, an instance of the bootstrap class is created,
* and a constructor is invoked.
* Otherwise, there must be a static method of the required name.
*/
String name() default ""; // empty string denotes a constructor with 'new'
/** The argument types of the bootstrap method, as passed out by the JVM.
* There is usually no reason to override the default.
*/
Class<?>[] arguments() default {Class.class, String.class, MethodType.class};
}
...@@ -25,56 +25,26 @@ ...@@ -25,56 +25,26 @@
package java.dyn; package java.dyn;
import sun.dyn.Access; import sun.dyn.*;
import sun.dyn.MemberName; import java.util.Collection;
import sun.dyn.CallSiteImpl;
/** /**
* A {@code CallSite} reifies an {@code invokedynamic} instruction from bytecode, * A {@code CallSite} is a holder for a variable {@link MethodHandle},
* and controls its linkage. * which is called its {@code target}.
* Every linked {@code CallSite} object corresponds to a distinct instance * Every call to a {@code CallSite} is delegated to the site's current target.
* of the {@code invokedynamic} instruction, and vice versa.
* <p> * <p>
* Every linked {@code CallSite} object has one state variable, * A call site is initially created in an <em>unlinked</em> state,
* a {@link MethodHandle} reference called the {@code target}. * which is distinguished by a null target variable.
* This reference is never null. Though it can change its value * Before the call site may be invoked (and before certain other
* successive values must always have exactly the {@link MethodType method type} * operations are attempted), the call site must be linked to
* called for by the bytecodes of the associated {@code invokedynamic} instruction * a non-null target.
* <p> * <p>
* It is the responsibility of each class's * A call site may be <em>relinked</em> by changing its target.
* {@link Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method} * The new target must be non-null and must have the same
* to produce call sites which have been pre-linked to an initial target method. * {@linkplain MethodHandle#type() type}
* The required {@link MethodType type} for the target method is a parameter * as the previous target.
* to each bootstrap method call. * Thus, though a call site can be relinked to a series of
* <p> * successive targets, it cannot change its type.
* The bootstrap method may elect to produce call sites of a
* language-specific subclass of {@code CallSite}. In such a case,
* the subclass may claim responsibility for initializing its target to
* a non-null value, by overriding {@link #initialTarget}.
* <p>
* An {@code invokedynamic} instruction which has not yet been executed
* is said to be <em>unlinked</em>. When an unlinked call site is executed,
* the containing class's bootstrap method is called to manufacture a call site,
* for the instruction. If the bootstrap method does not assign a non-null
* value to the new call site's target variable, the method {@link #initialTarget}
* is called to produce the new call site's first target method.
* <p>
* A freshly-created {@code CallSite} object is not yet in a linked state.
* An unlinked {@code CallSite} object reports null for its {@code callerClass}.
* When the JVM receives a {@code CallSite} object from a bootstrap method,
* it first ensures that its target is non-null and of the correct type.
* The JVM then links the {@code CallSite} object to the call site instruction,
* enabling the {@code callerClass} to return the class in which the instruction occurs.
* <p>
* Next, the JVM links the instruction to the {@code CallSite}, at which point
* any further execution of the {@code invokedynamic} instruction implicitly
* invokes the current target of the {@code CallSite} object.
* After this two-way linkage, both the instruction and the {@code CallSite}
* object are said to be linked.
* <p>
* This state of linkage continues until the method containing the
* dynamic call site is garbage collected, or the dynamic call site
* is invalidated by an explicit request.
* <p> * <p>
* Linkage happens once in the lifetime of any given {@code CallSite} object. * Linkage happens once in the lifetime of any given {@code CallSite} object.
* Because of call site invalidation, this linkage can be repeated for * Because of call site invalidation, this linkage can be repeated for
...@@ -87,6 +57,10 @@ import sun.dyn.CallSiteImpl; ...@@ -87,6 +57,10 @@ import sun.dyn.CallSiteImpl;
* Here is a sample use of call sites and bootstrap methods which links every * Here is a sample use of call sites and bootstrap methods which links every
* dynamic call site to print its arguments: * dynamic call site to print its arguments:
<blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java --> <blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java -->
&#064;BootstrapMethod(value=PrintArgsDemo.class, name="bootstrapDynamic")
static void test() throws Throwable {
InvokeDynamic.baz("baz arg", 2, 3.14);
}
private static void printArgs(Object... args) { private static void printArgs(Object... args) {
System.out.println(java.util.Arrays.deepToString(args)); System.out.println(java.util.Arrays.deepToString(args));
} }
...@@ -96,17 +70,16 @@ static { ...@@ -96,17 +70,16 @@ static {
Class thisClass = lookup.lookupClass(); // (who am I?) Class thisClass = lookup.lookupClass(); // (who am I?)
printArgs = lookup.findStatic(thisClass, printArgs = lookup.findStatic(thisClass,
"printArgs", MethodType.methodType(void.class, Object[].class)); "printArgs", MethodType.methodType(void.class, Object[].class));
Linkage.registerBootstrapMethod("bootstrapDynamic");
} }
private static CallSite bootstrapDynamic(Class caller, String name, MethodType type) { private static CallSite bootstrapDynamic(Class caller, String name, MethodType type) {
// ignore caller and name, but match the type: // ignore caller and name, but match the type:
return new CallSite(MethodHandles.collectArguments(printArgs, type)); return new CallSite(MethodHandles.collectArguments(printArgs, type));
} }
</pre></blockquote> </pre></blockquote>
* @see Linkage#registerBootstrapMethod(java.lang.Class, java.dyn.MethodHandle)
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
public class CallSite public class CallSite
implements MethodHandleProvider
{ {
private static final Access IMPL_TOKEN = Access.getToken(); private static final Access IMPL_TOKEN = Access.getToken();
...@@ -209,6 +182,7 @@ public class CallSite ...@@ -209,6 +182,7 @@ public class CallSite
* {@code InvokeDynamicBootstrapError}, which in turn causes the * {@code InvokeDynamicBootstrapError}, which in turn causes the
* linkage of the {@code invokedynamic} instruction to terminate * linkage of the {@code invokedynamic} instruction to terminate
* abnormally. * abnormally.
* @deprecated transitional form defined in EDR but removed in PFD
*/ */
protected MethodHandle initialTarget(Class<?> callerClass, String name, MethodType type) { protected MethodHandle initialTarget(Class<?> callerClass, String name, MethodType type) {
throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+name+type); throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+name+type);
...@@ -278,16 +252,44 @@ public class CallSite ...@@ -278,16 +252,44 @@ public class CallSite
*/ */
@Override @Override
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder("CallSite#"); return "CallSite"+(target == null ? "" : target.type());
buf.append(hashCode()); }
if (!isLinked())
buf.append("[unlinked]"); /**
else * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
buf.append("[") * Produce a method handle equivalent to an invokedynamic instruction
.append("from ").append(vmmethod.getDeclaringClass().getName()) * which has been linked to this call site.
.append(" : ").append(getTarget().type()) * <p>If this call site is a {@link ConstantCallSite}, this method
.append(" => ").append(getTarget()) * simply returns the call site's target, since that will not change.
.append("]"); * <p>Otherwise, this method is equivalent to the following code:
return buf.toString(); * <p><blockquote><pre>
* MethodHandle getTarget, invoker, result;
* getTarget = MethodHandles.lookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
* invoker = MethodHandles.exactInvoker(this.type());
* result = MethodHandles.foldArguments(invoker, getTarget)
* </pre></blockquote>
* @return a method handle which always invokes this call site's current target
*/
public final MethodHandle dynamicInvoker() {
if (this instanceof ConstantCallSite)
return getTarget(); // will not change dynamically
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget);
} }
private static final MethodHandle GET_TARGET;
static {
try {
GET_TARGET = MethodHandles.Lookup.IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
} catch (NoAccessException ignore) {
throw new InternalError();
}
}
/** Implementation of {@link MethodHandleProvider} which returns {@code this.dynamicInvoker()}. */
public final MethodHandle asMethodHandle() { return dynamicInvoker(); }
/** Implementation of {@link MethodHandleProvider}, which returns {@code this.dynamicInvoker().asType(type)}. */
public final MethodHandle asMethodHandle(MethodType type) { return dynamicInvoker().asType(type); }
} }
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.dyn;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* Lazily associate a computed value with (potentially) every class.
* @author John Rose, JSR 292 EG
*/
public abstract class ClassValue<T> {
/**
* Compute the given class's derived value for this {@code ClassValue}.
* <p>
* This method will be invoked within the first thread that accesses
* the value with the {@link #get}.
* <p>
* Normally, this method is invoked at most once per class,
* but it may be invoked again in case of subsequent invocations
* of {@link #remove} followed by {@link #get}.
*
* @return the computed value for this thread-local
*/
protected abstract T computeValue(Class<?> type);
/**
* Creates a new class value.
*/
protected ClassValue() {
}
/**
* Returns the value for the given class.
* If no value has yet been computed, it is obtained by
* by an invocation of the {@link #computeValue} method.
* <p>
* The actual installation of the value on the class
* is performed while the class's synchronization lock
* is held. At that point, if racing threads have
* computed values, one is chosen, and returned to
* all the racing threads.
*
* @return the current thread's value of this thread-local
*/
public T get(Class<?> type) {
ClassValueMap map = getMap(type);
if (map != null) {
Object x = map.get(this);
if (x != null) {
return (T) map.unmaskNull(x);
}
}
return setComputedValue(type);
}
/**
* Removes the associated value for the given class.
* If this value is subsequently {@linkplain #get read} for the same class,
* its value will be reinitialized by invoking its {@link #computeValue} method.
* This may result in an additional invocation of the
* {@code computeValue} method for the given class.
*/
public void remove(Class<?> type) {
ClassValueMap map = getMap(type);
if (map != null) {
synchronized (map) {
map.remove(this);
}
}
}
/// Implementation...
/** The hash code for this type is based on the identity of the object,
* and is well-dispersed for power-of-two tables.
*/
public final int hashCode() { return hashCode; }
private final int hashCode = HASH_CODES.getAndAdd(0x61c88647);
private static final AtomicInteger HASH_CODES = new AtomicInteger();
private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
/** Slow path for {@link #get}. */
private T setComputedValue(Class<?> type) {
ClassValueMap map = getMap(type);
if (map == null) {
map = initializeMap(type);
}
T value = computeValue(type);
STORE_BARRIER.lazySet(0);
// All stores pending from computeValue are completed.
synchronized (map) {
// Warm up the table with a null entry.
map.preInitializeEntry(this);
}
// All stores pending from table expansion are completed.
synchronized (map) {
value = (T) map.initializeEntry(this, value);
// One might fear a possible race condition here
// if the code for map.put has flushed the write
// to map.table[*] before the writes to the Map.Entry
// are done. This is not possible, since we have
// warmed up the table with an empty entry.
}
return value;
}
// Replace this map by a per-class slot.
private static final WeakHashMap<Class<?>, ClassValueMap> ROOT
= new WeakHashMap<Class<?>, ClassValueMap>();
private static ClassValueMap getMap(Class<?> type) {
return ROOT.get(type);
}
private static ClassValueMap initializeMap(Class<?> type) {
synchronized (ClassValue.class) {
ClassValueMap map = ROOT.get(type);
if (map == null)
ROOT.put(type, map = new ClassValueMap());
return map;
}
}
static class ClassValueMap extends WeakHashMap<ClassValue, Object> {
/** Make sure this table contains an Entry for the given key, even if it is empty. */
void preInitializeEntry(ClassValue key) {
if (!this.containsKey(key))
this.put(key, null);
}
/** Make sure this table contains a non-empty Entry for the given key. */
Object initializeEntry(ClassValue key, Object value) {
Object prior = this.get(key);
if (prior != null) {
return unmaskNull(prior);
}
this.put(key, maskNull(value));
return value;
}
Object maskNull(Object x) {
return x == null ? this : x;
}
Object unmaskNull(Object x) {
return x == this ? null : x;
}
}
}
/* /*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,71 +23,21 @@ ...@@ -23,71 +23,21 @@
* questions. * questions.
*/ */
package sun.java2d.pisces; package java.dyn;
/** /**
* The <code>LineSink</code> interface accepts a series of line * A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed.
* drawing commands: <code>moveTo</code>, <code>lineTo</code>, * The only way to relink an {@code invokedynamic} instruction bound to a {@code ConstantCallSite} is
* <code>close</code> (equivalent to a <code>lineTo</code> command * to invalidate the instruction as a whole.
* with an argument equal to the argument of the last * @author John Rose, JSR 292 EG
* <code>moveTo</code> command), and <code>end</code>.
*
* <p> A <code>Flattener</code> may be used to connect a general path
* source to a <code>LineSink</code>.
*
* <p> The <code>Renderer</code> class implements the
* <code>LineSink</code> interface.
*
*/ */
public interface LineSink { public class ConstantCallSite extends CallSite {
/** Create a call site with a permanent target. */
/** public ConstantCallSite(MethodHandle target) {
* Moves the current drawing position to the point <code>(x0, super(target);
* y0)</code>. }
* /** Throw an {@link IllegalArgumentException}, because this kind of call site cannot change its target. */
* @param x0 the X coordinate @Override public final void setTarget(MethodHandle ignore) {
* @param y0 the Y coordinate throw new IllegalArgumentException("ConstantCallSite");
*/ }
public void moveTo(float x0, float y0);
/**
* Provides a hint that the current segment should be joined to
* the following segment using an explicit miter or round join if
* required.
*
* <p> An application-generated path will generally have no need
* to contain calls to this method; they are typically introduced
* by a <code>Flattener</code> to mark segment divisions that
* appear in its input, and consumed by a <code>Stroker</code>
* that is responsible for emitting the miter or round join
* segments.
*
* <p> Other <code>LineSink</code> classes should simply pass this
* hint to their output sink as needed.
*/
public void lineJoin();
/**
* Draws a line from the current drawing position to the point
* <code>(x1, y1)</code> and sets the current drawing position to
* <code>(x1, y1)</code>.
*
* @param x1 the X coordinate
* @param y1 the Y coordinate
*/
public void lineTo(float x1, float y1);
/**
* Closes the current path by drawing a line from the current
* drawing position to the point specified by the moset recent
* <code>moveTo</code> command.
*/
public void close();
/**
* Ends the current path. It may be necessary to end a path in
* order to allow end caps to be drawn.
*/
public void end();
} }
...@@ -35,7 +35,7 @@ package java.dyn; ...@@ -35,7 +35,7 @@ package java.dyn;
* The target method is a property of the reified {@linkplain CallSite call site object} * The target method is a property of the reified {@linkplain CallSite call site object}
* which is linked to each active {@code invokedynamic} instruction. * which is linked to each active {@code invokedynamic} instruction.
* The call site object is initially produced by a * The call site object is initially produced by a
* {@linkplain java.dyn.Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method} * {@linkplain BootstrapMethod bootstrap method}
* associated with the class whose bytecodes include the dynamic call site. * associated with the class whose bytecodes include the dynamic call site.
* <p> * <p>
* The type {@code InvokeDynamic} has no particular meaning as a * The type {@code InvokeDynamic} has no particular meaning as a
...@@ -45,22 +45,31 @@ package java.dyn; ...@@ -45,22 +45,31 @@ package java.dyn;
* It may be imported for ease of use. * It may be imported for ease of use.
* <p> * <p>
* Here are some examples: * Here are some examples:
* <p><blockquote><pre> <blockquote><pre><!-- see indy-demo/src/JavaDocExamples.java -->
* Object x; String s; int i; &#064;BootstrapMethod(value=Here.class, name="bootstrapDynamic")
* x = InvokeDynamic.greet("world"); // greet(Ljava/lang/String;)Ljava/lang/Object; static void example() throws Throwable {
* s = InvokeDynamic.&lt;String&gt;hail(x); // hail(Ljava/lang/Object;)Ljava/lang/String; Object x; String s; int i;
* InvokeDynamic.&lt;void&gt;cogito(); // cogito()V x = InvokeDynamic.greet("world"); // greet(Ljava/lang/String;)Ljava/lang/Object;
* i = InvokeDynamic.&lt;int&gt;#"op:+"(2, 3); // "op:+"(II)I s = (String) InvokeDynamic.hail(x); // hail(Ljava/lang/Object;)Ljava/lang/String;
* </pre></blockquote> InvokeDynamic.cogito(); // cogito()V
i = (int) InvokeDynamic.#"op:+"(2, 3); // "op:+"(II)I
}
static MethodHandle bootstrapDynamic(Class caller, String name, MethodType type) { ... }
</pre></blockquote>
* Each of the above calls generates a single invokedynamic instruction * Each of the above calls generates a single invokedynamic instruction
* with the name-and-type descriptors indicated in the comments. * with the name-and-type descriptors indicated in the comments.
* <p>
* The argument types are taken directly from the actual arguments, * The argument types are taken directly from the actual arguments,
* while the return type is taken from the type parameter. * while the return type corresponds to the target of the assignment.
* (This type parameter may be a primtive, and it defaults to {@code Object}.) * (Currently, the return type must be given as a false type parameter.
* This type parameter is an irregular use of the generic type syntax,
* and is likely to change in favor of a convention based on target typing.)
* <p>
* The final example uses a special syntax for uttering non-Java names. * The final example uses a special syntax for uttering non-Java names.
* Any name legal to the JVM may be given between the double quotes. * Any name legal to the JVM may be given between the double quotes.
* <p>
* None of these calls is complete without a bootstrap method, * None of these calls is complete without a bootstrap method,
* which must be registered by the static initializer of the enclosing class. * which must be declared for the enclosing class or method.
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
@MethodHandle.PolymorphicSignature @MethodHandle.PolymorphicSignature
......
...@@ -28,15 +28,11 @@ package java.dyn; ...@@ -28,15 +28,11 @@ package java.dyn;
/** /**
* Thrown to indicate that an {@code invokedynamic} instruction has * Thrown to indicate that an {@code invokedynamic} instruction has
* failed to find its * failed to find its
* {@linkplain Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method}, * {@linkplain BootstrapMethod bootstrap method},
* or the bootstrap method has * or the bootstrap method has
* failed to provide a * failed to provide a
* {@linkplain CallSite} call site with a non-null {@linkplain MethodHandle target} * {@linkplain CallSite} call site with a non-null {@linkplain MethodHandle target}
* of the correct {@linkplain MethodType method type}. * of the correct {@linkplain MethodType method type}.
* <p>
* The bootstrap method must have been declared during a class's initialization
* by a call to one of the overloadings of
* {@link Linkage#registerBootstrapMethod registerBootstrapMethod}.
* *
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
* @since 1.7 * @since 1.7
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package java.dyn; package java.dyn;
import java.lang.annotation.Annotation;
import java.dyn.MethodHandles.Lookup; import java.dyn.MethodHandles.Lookup;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import sun.dyn.Access; import sun.dyn.Access;
...@@ -56,11 +55,7 @@ public class Linkage { ...@@ -56,11 +55,7 @@ public class Linkage {
* <li>the class containing the {@code invokedynamic} instruction, for which the bootstrap method was registered * <li>the class containing the {@code invokedynamic} instruction, for which the bootstrap method was registered
* <li>the name of the method being invoked (a {@link String}) * <li>the name of the method being invoked (a {@link String})
* <li>the type of the method being invoked (a {@link MethodType}) * <li>the type of the method being invoked (a {@link MethodType})
* <li><em>TBD</em> optionally, an unordered array of {@link Annotation}s attached to the call site
* <em>(Until this feature is implemented, this will always receive an empty array.)</em>
* </ul> * </ul>
* <em>(TBD: The final argument type may be missing from the method handle's type.
* Additional arguments may be added in the future.)</em>
* The bootstrap method acts as a factory method which accepts the given arguments * The bootstrap method acts as a factory method which accepts the given arguments
* and returns a {@code CallSite} object (possibly of a subclass of {@code CallSite}). * and returns a {@code CallSite} object (possibly of a subclass of {@code CallSite}).
* <p> * <p>
...@@ -86,6 +81,7 @@ public class Linkage { ...@@ -86,6 +81,7 @@ public class Linkage {
* or is already running in another thread * or is already running in another thread
* @exception SecurityException if there is a security manager installed, * @exception SecurityException if there is a security manager installed,
* and a {@link LinkagePermission} check fails for "registerBootstrapMethod" * and a {@link LinkagePermission} check fails for "registerBootstrapMethod"
* @deprecated Use @{@link BootstrapMethod} annotations instead
*/ */
public static public static
void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) { void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) {
...@@ -97,14 +93,9 @@ public class Linkage { ...@@ -97,14 +93,9 @@ public class Linkage {
static private void checkBSM(MethodHandle mh) { static private void checkBSM(MethodHandle mh) {
if (mh == null) throw newIllegalArgumentException("null bootstrap method"); if (mh == null) throw newIllegalArgumentException("null bootstrap method");
if (mh.type() == BOOTSTRAP_METHOD_TYPE_2)
// For now, always pass an empty array for the Annotations argument
mh = MethodHandles.insertArguments(mh, BOOTSTRAP_METHOD_TYPE_2.parameterCount()-1,
(Object)NO_ANNOTATIONS);
if (mh.type() == BOOTSTRAP_METHOD_TYPE) return; if (mh.type() == BOOTSTRAP_METHOD_TYPE) return;
throw new WrongMethodTypeException(mh.toString()); throw new WrongMethodTypeException(mh.toString());
} }
static private final Annotation[] NO_ANNOTATIONS = { };
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em> * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
...@@ -115,6 +106,7 @@ public class Linkage { ...@@ -115,6 +106,7 @@ public class Linkage {
* @throws NoSuchMethodException if there is no such method * @throws NoSuchMethodException if there is no such method
* @throws IllegalStateException if the caller class's static initializer * @throws IllegalStateException if the caller class's static initializer
* has already run, or is already running in another thread * has already run, or is already running in another thread
* @deprecated Use @{@link BootstrapMethod} annotations instead
*/ */
public static public static
void registerBootstrapMethod(Class<?> runtime, String name) { void registerBootstrapMethod(Class<?> runtime, String name) {
...@@ -131,6 +123,7 @@ public class Linkage { ...@@ -131,6 +123,7 @@ public class Linkage {
* @throws IllegalArgumentException if there is no such method * @throws IllegalArgumentException if there is no such method
* @throws IllegalStateException if the caller class's static initializer * @throws IllegalStateException if the caller class's static initializer
* has already run, or is already running in another thread * has already run, or is already running in another thread
* @deprecated Use @{@link BootstrapMethod} annotations instead
*/ */
public static public static
void registerBootstrapMethod(String name) { void registerBootstrapMethod(String name) {
...@@ -142,18 +135,10 @@ public class Linkage { ...@@ -142,18 +135,10 @@ public class Linkage {
void registerBootstrapMethodLookup(Class<?> callerClass, Class<?> runtime, String name) { void registerBootstrapMethodLookup(Class<?> callerClass, Class<?> runtime, String name) {
Lookup lookup = new Lookup(IMPL_TOKEN, callerClass); Lookup lookup = new Lookup(IMPL_TOKEN, callerClass);
MethodHandle bootstrapMethod; MethodHandle bootstrapMethod;
// Try both types. TBD
try { try {
bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE_2); bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
bootstrapMethod = null; throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex);
}
if (bootstrapMethod == null) {
try {
bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
} catch (NoAccessException ex) {
throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex);
}
} }
checkBSM(bootstrapMethod); checkBSM(bootstrapMethod);
MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod); MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
...@@ -172,6 +157,7 @@ public class Linkage { ...@@ -172,6 +157,7 @@ public class Linkage {
* and the immediate caller of this method is not in the same * and the immediate caller of this method is not in the same
* package as the caller class * package as the caller class
* and a {@link LinkagePermission} check fails for "getBootstrapMethod" * and a {@link LinkagePermission} check fails for "getBootstrapMethod"
* @deprecated
*/ */
public static public static
MethodHandle getBootstrapMethod(Class callerClass) { MethodHandle getBootstrapMethod(Class callerClass) {
...@@ -188,10 +174,6 @@ public class Linkage { ...@@ -188,10 +174,6 @@ public class Linkage {
public static final MethodType BOOTSTRAP_METHOD_TYPE public static final MethodType BOOTSTRAP_METHOD_TYPE
= MethodType.methodType(CallSite.class, = MethodType.methodType(CallSite.class,
Class.class, String.class, MethodType.class); Class.class, String.class, MethodType.class);
static final MethodType BOOTSTRAP_METHOD_TYPE_2
= MethodType.methodType(CallSite.class,
Class.class, String.class, MethodType.class,
Annotation[].class);
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em> * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
......
...@@ -31,6 +31,7 @@ import java.util.Hashtable; ...@@ -31,6 +31,7 @@ import java.util.Hashtable;
import java.util.StringTokenizer; import java.util.StringTokenizer;
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* This class is for managing runtime permission checking for * This class is for managing runtime permission checking for
* operations performed by methods in the {@link Linkage} class. * operations performed by methods in the {@link Linkage} class.
* Like a {@link RuntimePermission}, on which it is modeled, * Like a {@link RuntimePermission}, on which it is modeled,
...@@ -52,13 +53,6 @@ import java.util.StringTokenizer; ...@@ -52,13 +53,6 @@ import java.util.StringTokenizer;
* </tr> * </tr>
* *
* <tr> * <tr>
* <td>registerBootstrapMethod.{class name}</td>
* <td>Specifying a bootstrap method for {@code invokedynamic} instructions within a class of the given name</td>
* <td>An attacker could attempt to attach a bootstrap method to a class which
* has just been loaded, thus gaining control of its {@code invokedynamic} calls.</td>
* </tr>
*
* <tr>
* <td>invalidateAll</td> * <td>invalidateAll</td>
* <td>Force the relinking of invokedynamic call sites everywhere.</td> * <td>Force the relinking of invokedynamic call sites everywhere.</td>
* <td>This could allow an attacker to slow down the system, * <td>This could allow an attacker to slow down the system,
...@@ -73,8 +67,9 @@ import java.util.StringTokenizer; ...@@ -73,8 +67,9 @@ import java.util.StringTokenizer;
* <td>See {@code invalidateAll}.</td> * <td>See {@code invalidateAll}.</td>
* </tr> * </tr>
* </table> * </table>
* <p>ISSUE: Is this still needed?
* *
* @see java.security.RuntimePermission * @see java.lang.RuntimePermission
* @see java.lang.SecurityManager * @see java.lang.SecurityManager
* *
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
...@@ -86,7 +81,7 @@ public final class LinkagePermission extends BasicPermission { ...@@ -86,7 +81,7 @@ public final class LinkagePermission extends BasicPermission {
/** /**
* Create a new LinkagePermission with the given name. * Create a new LinkagePermission with the given name.
* The name is the symbolic name of the LinkagePermission, such as * The name is the symbolic name of the LinkagePermission, such as
* "registerBootstrapMethod", "invalidateCallerClass.*", etc. An asterisk * "invalidateCallerClass.*", etc. An asterisk
* may appear at the end of the name, following a ".", or by itself, to * may appear at the end of the name, following a ".", or by itself, to
* signify a wildcard match. * signify a wildcard match.
* *
......
/* /*
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -36,11 +36,13 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility ...@@ -36,11 +36,13 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility
/** /**
* A method handle is a typed, directly executable reference to a method, * A method handle is a typed, directly executable reference to a method,
* constructor, field, or similar low-level operation, with optional * constructor, field, or similar low-level operation, with optional
* conversion or substitution of arguments or return values. * transformations of arguments or return values.
* (These transformations include conversion, insertion, deletion,
* substitution. See the methods of this class and of {@link MethodHandles}.)
* <p> * <p>
* Method handles are strongly typed according to signature. * Method handles are strongly typed according to signature.
* They are not distinguished by method name or enclosing class. * They are not distinguished by method name or enclosing class.
* A method handle must be invoked under a signature which exactly matches * A method handle must be invoked under a signature which matches
* the method handle's own {@link MethodType method type}. * the method handle's own {@link MethodType method type}.
* <p> * <p>
* Every method handle confesses its type via the {@code type} accessor. * Every method handle confesses its type via the {@code type} accessor.
...@@ -174,9 +176,10 @@ assert(i == 3); ...@@ -174,9 +176,10 @@ assert(i == 3);
* merely a documentation convention. These type parameters do * merely a documentation convention. These type parameters do
* not play a role in type-checking method handle invocations. * not play a role in type-checking method handle invocations.
* <p> * <p>
* Note: Like classes and strings, method handles that correspond directly * Like classes and strings, method handles that correspond to accessible
* to fields and methods can be represented directly as constants to be * fields, methods, and constructors can be represented directly
* loaded by {@code ldc} bytecodes. * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
* Loading such a constant causes the component classes of its type to be loaded as necessary.
* *
* @see MethodType * @see MethodType
* @see MethodHandles * @see MethodHandles
...@@ -186,6 +189,7 @@ public abstract class MethodHandle ...@@ -186,6 +189,7 @@ public abstract class MethodHandle
// Note: This is an implementation inheritance hack, and will be removed // Note: This is an implementation inheritance hack, and will be removed
// with a JVM change which moves the required hidden state onto this class. // with a JVM change which moves the required hidden state onto this class.
extends MethodHandleImpl extends MethodHandleImpl
implements MethodHandleProvider
{ {
private static Access IMPL_TOKEN = Access.getToken(); private static Access IMPL_TOKEN = Access.getToken();
...@@ -197,7 +201,7 @@ public abstract class MethodHandle ...@@ -197,7 +201,7 @@ public abstract class MethodHandle
* those methods which are signature polymorphic. * those methods which are signature polymorphic.
*/ */
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.TYPE}) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@interface PolymorphicSignature { } @interface PolymorphicSignature { }
private MethodType type; private MethodType type;
...@@ -274,10 +278,14 @@ public abstract class MethodHandle ...@@ -274,10 +278,14 @@ public abstract class MethodHandle
* and performing simple conversions for arguments and return types. * and performing simple conversions for arguments and return types.
* The signature at the call site of {@code invokeGeneric} must * The signature at the call site of {@code invokeGeneric} must
* have the same arity as this method handle's {@code type}. * have the same arity as this method handle's {@code type}.
* The same conversions are allowed on arguments or return values as are supported by * <p>
* by {@link MethodHandles#convertArguments}.
* If the call site signature exactly matches this method handle's {@code type}, * If the call site signature exactly matches this method handle's {@code type},
* the call proceeds as if by {@link #invokeExact}. * the call proceeds as if by {@link #invokeExact}.
* <p>
* Otherwise, the call proceeds as if this method handle were first
* adjusted by calling {@link #asType} to adjust this method handle
* to the required type, and then the call proceeds as if by
* {@link #invokeExact} on the adjusted method handle.
*/ */
public final native @PolymorphicSignature <R,A> R invokeGeneric(A... args) throws Throwable; public final native @PolymorphicSignature <R,A> R invokeGeneric(A... args) throws Throwable;
...@@ -538,4 +546,10 @@ public abstract class MethodHandle ...@@ -538,4 +546,10 @@ public abstract class MethodHandle
public final MethodHandle bindTo(Object x) { public final MethodHandle bindTo(Object x) {
return MethodHandles.insertArguments(this, 0, x); return MethodHandles.insertArguments(this, 0, x);
} }
/** Implementation of {@link MethodHandleProvider}, which returns {@code this}. */
public final MethodHandle asMethodHandle() { return this; }
/** Implementation of {@link MethodHandleProvider}, which returns {@code this.asType(type)}. */
public final MethodHandle asMethodHandle(MethodType type) { return this.asType(type); }
} }
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.dyn;
/**
* An interface for an object to provide a target {@linkplain MethodHandle method handle} to a {@code invokedynamic} instruction.
* There are many function-like objects in various Java APIs.
* This interface provides a standard way for such function-like objects to be bound
* to a dynamic call site, by providing a view of their behavior in the form of a low-level method handle.
* <p>
* The type {@link MethodHandle} is a concrete class whose implementation
* hierarchy (if any) may be tightly coupled to the underlying JVM implementation.
* It cannot also serve as a base type for user-defined functional APIs.
* For this reason, {@code MethodHandle} cannot be subclassed to add new
* behavior to method handles. But this interface can be used to provide
* a link between a user-defined function and the {@code invokedynamic}
* instruction and the method handle API.
*/
public interface MethodHandleProvider {
/** Produce a method handle which will serve as a behavioral proxy for the current object.
* The type and invocation behavior of the proxy method handle are user-defined,
* and should have some relation to the intended meaning of the original object itself.
* <p>
* The current object may have a changeable behavior.
* For example, {@link CallSite} has a {@code setTarget} method which changes its invocation.
* In such a case, it is <em>incorrect</em> for {@code asMethodHandle} to return
* a method handle whose behavior may diverge from that of the current object.
* Rather, the returned method handle must stably and permanently access
* the behavior of the current object, even if that behavior is changeable.
* <p>
* The reference identity of the proxy method handle is not guaranteed to
* have any particular relation to the reference identity of the object.
* In particular, several objects with the same intended meaning could
* share a common method handle, or the same object could return different
* method handles at different times. In the latter case, the different
* method handles should have the same type and invocation behavior,
* and be usable from any thread at any time.
* In particular, if a MethodHandleProvider is bound to an <code>invokedynamic</code>
* call site, the proxy method handle extracted at the time of binding
* will be used for an unlimited time, until the call site is rebound.
* <p>
* The type {@link MethodHandle} itself implements {@code MethodHandleProvider}, and
* for this method simply returns {@code this}.
*/
public MethodHandle asMethodHandle();
/** Produce a method handle of a given type which will serve as a behavioral proxy for the current object.
* As for the no-argument version {@link #asMethodHandle()}, the invocation behavior of the
* proxy method handle is user-defined. But the type must be the given type,
* or else a {@link WrongMethodTypeException} must be thrown.
* <p>
* If the current object somehow represents a variadic or overloaded behavior,
* the method handle returned for a given type might represent only a subset of
* the current object's repertoire of behaviors, which correspond to that type.
*/
public MethodHandle asMethodHandle(MethodType type) throws WrongMethodTypeException;
}
...@@ -25,15 +25,12 @@ ...@@ -25,15 +25,12 @@
package java.dyn; package java.dyn;
import java.lang.reflect.Constructor; import java.lang.reflect.*;
import sun.dyn.Access; import sun.dyn.Access;
import sun.dyn.MemberName; import sun.dyn.MemberName;
import sun.dyn.MethodHandleImpl; import sun.dyn.MethodHandleImpl;
import sun.dyn.util.VerifyAccess; import sun.dyn.util.VerifyAccess;
import sun.dyn.util.Wrapper; import sun.dyn.util.Wrapper;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
...@@ -81,6 +78,14 @@ public class MethodHandles { ...@@ -81,6 +78,14 @@ public class MethodHandles {
* Return a {@link Lookup lookup object} which is trusted minimally. * Return a {@link Lookup lookup object} which is trusted minimally.
* It can only be used to create method handles to * It can only be used to create method handles to
* publicly accessible fields and methods. * publicly accessible fields and methods.
* <p>
* As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class}
* of this lookup object will be {@link java.lang.Object}.
* <p>
* The lookup class can be changed to any other class {@code C} using an expression of the form
* {@linkplain Lookup#in <code>publicLookup().in(C.class)</code>}.
* Since all classes have equal access to public names,
* such a change would confer no new access rights.
*/ */
public static Lookup publicLookup() { public static Lookup publicLookup() {
return Lookup.PUBLIC_LOOKUP; return Lookup.PUBLIC_LOOKUP;
...@@ -90,9 +95,10 @@ public class MethodHandles { ...@@ -90,9 +95,10 @@ public class MethodHandles {
* A <em>lookup object</em> is a factory for creating method handles, * A <em>lookup object</em> is a factory for creating method handles,
* when the creation requires access checking. * when the creation requires access checking.
* Method handles do not perform * Method handles do not perform
* access checks when they are called; this is a major difference * access checks when they are called, but rather when they are created.
* (This is a major difference
* from reflective {@link Method}, which performs access checking * from reflective {@link Method}, which performs access checking
* against every caller, on every call. * against every caller, on every call.)
* Therefore, method handle access * Therefore, method handle access
* restrictions must be enforced when a method handle is created. * restrictions must be enforced when a method handle is created.
* The caller class against which those restrictions are enforced * The caller class against which those restrictions are enforced
...@@ -107,7 +113,7 @@ public class MethodHandles { ...@@ -107,7 +113,7 @@ public class MethodHandles {
* It may then use this factory to create method handles on * It may then use this factory to create method handles on
* all of its methods, including private ones. * all of its methods, including private ones.
* It may also delegate the lookup (e.g., to a metaobject protocol) * It may also delegate the lookup (e.g., to a metaobject protocol)
* by passing the {@code Lookup} object to other code. * by passing the lookup object to other code.
* If this other code creates method handles, they will be access * If this other code creates method handles, they will be access
* checked against the original lookup class, and not with any higher * checked against the original lookup class, and not with any higher
* privileges. * privileges.
...@@ -125,23 +131,28 @@ public class MethodHandles { ...@@ -125,23 +131,28 @@ public class MethodHandles {
* It can also fail if a security manager is installed and refuses * It can also fail if a security manager is installed and refuses
* access. In any of these cases, an exception will be * access. In any of these cases, an exception will be
* thrown from the attempted lookup. * thrown from the attempted lookup.
* <p>
* In general, the conditions under which a method handle may be * In general, the conditions under which a method handle may be
* created for a method {@code M} are exactly as restrictive as the conditions * created for a method {@code M} are exactly as restrictive as the conditions
* under which the lookup class could have compiled a call to {@code M}. * under which the lookup class could have compiled a call to {@code M}.
* At least some of these error conditions are likely to be * This rule is applied even if the Java compiler might have created
* represented by checked exceptions in the final version of this API. * an wrapper method to access a private method of another class
* in the same top-level declaration.
* For example, a lookup object created for a nested class {@code C.D}
* can access private members within other related classes such as
* {@code C}, {@code C.D.E}, or {@code C.B}.
*/ */
public static final public static final
class Lookup { class Lookup {
/** The class on behalf of whom the lookup is being performed. */ /** The class on behalf of whom the lookup is being performed. */
private final Class<?> lookupClass; private final Class<?> lookupClass;
/** The allowed sorts of members which may be looked up (public, etc.), with STRICT for package. */ /** The allowed sorts of members which may be looked up (public, etc.), with STATIC for package. */
private final int allowedModes; private final int allowedModes;
private static final int private static final int
PUBLIC = Modifier.PUBLIC, PUBLIC = Modifier.PUBLIC,
PACKAGE = Modifier.STRICT, PACKAGE = Modifier.STATIC,
PROTECTED = Modifier.PROTECTED, PROTECTED = Modifier.PROTECTED,
PRIVATE = Modifier.PRIVATE, PRIVATE = Modifier.PRIVATE,
ALL_MODES = (PUBLIC | PACKAGE | PROTECTED | PRIVATE), ALL_MODES = (PUBLIC | PACKAGE | PROTECTED | PRIVATE),
...@@ -155,8 +166,10 @@ public class MethodHandles { ...@@ -155,8 +166,10 @@ public class MethodHandles {
/** Which class is performing the lookup? It is this class against /** Which class is performing the lookup? It is this class against
* which checks are performed for visibility and access permissions. * which checks are performed for visibility and access permissions.
* <p> * <p>
* This value is null if and only if this lookup was produced * The class implies a maximum level of access permission,
* by {@link MethodHandles#publicLookup}. * but the permissions may be additionally limited by the bitmask
* {@link #lookupModes}, which controls whether non-public members
* can be accessed.
*/ */
public Class<?> lookupClass() { public Class<?> lookupClass() {
return lookupClass; return lookupClass;
...@@ -168,10 +181,15 @@ public class MethodHandles { ...@@ -168,10 +181,15 @@ public class MethodHandles {
} }
/** Which types of members can this lookup object produce? /** Which types of members can this lookup object produce?
* The result is a bit-mask of the modifier bits PUBLIC, PROTECTED, PRIVATE, and STRICT. * The result is a bit-mask of the {@link Modifier} bits
* The modifier bit STRICT stands in for the (non-existent) package protection mode. * {@linkplain Modifier#PUBLIC PUBLIC (0x01)},
* {@linkplain Modifier#PROTECTED PROTECTED (0x02)},
* {@linkplain Modifier#PRIVATE PRIVATE (0x04)},
* and {@linkplain Modifier#STATIC STATIC (0x08)}.
* The modifier bit {@code STATIC} stands in for the package protection mode,
* which does not have an explicit modifier bit.
*/ */
int lookupModes() { public int lookupModes() {
return allowedModes & ALL_MODES; return allowedModes & ALL_MODES;
} }
...@@ -621,32 +639,32 @@ public class MethodHandles { ...@@ -621,32 +639,32 @@ public class MethodHandles {
/// Helper methods, all package-private. /// Helper methods, all package-private.
MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) { MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve checkSymbolicClass(refc); // do this before attempting to resolve
int mods = (isStatic ? Modifier.STATIC : 0); int mods = (isStatic ? Modifier.STATIC : 0);
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull()); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
} }
MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) { MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve checkSymbolicClass(refc); // do this before attempting to resolve
int mods = (isStatic ? Modifier.STATIC : 0); int mods = (isStatic ? Modifier.STATIC : 0);
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull()); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
} }
MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic, MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic,
boolean searchSupers, Class<?> specialCaller) { boolean searchSupers, Class<?> specialCaller) throws NoAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve checkSymbolicClass(refc); // do this before attempting to resolve
int mods = (isStatic ? Modifier.STATIC : 0); int mods = (isStatic ? Modifier.STATIC : 0);
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller);
} }
void checkSymbolicClass(Class<?> refc) { void checkSymbolicClass(Class<?> refc) throws NoAccessException {
Class<?> caller = lookupClassOrNull(); Class<?> caller = lookupClassOrNull();
if (caller != null && !VerifyAccess.isClassAccessible(refc, caller)) if (caller != null && !VerifyAccess.isClassAccessible(refc, caller))
throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller); throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller);
} }
void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) { void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws NoAccessException {
String message; String message;
if (m.isConstructor()) if (m.isConstructor())
message = "expected a method, not a constructor"; message = "expected a method, not a constructor";
...@@ -659,7 +677,7 @@ public class MethodHandles { ...@@ -659,7 +677,7 @@ public class MethodHandles {
throw newNoAccessException(message, m, lookupClass()); throw newNoAccessException(message, m, lookupClass());
} }
void checkAccess(Class<?> refc, MemberName m) { void checkAccess(Class<?> refc, MemberName m) throws NoAccessException {
int allowedModes = this.allowedModes; int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return; if (allowedModes == TRUSTED) return;
int mods = m.getModifiers(); int mods = m.getModifiers();
...@@ -695,14 +713,14 @@ public class MethodHandles { ...@@ -695,14 +713,14 @@ public class MethodHandles {
return "member is private to package"; return "member is private to package";
} }
void checkSpecialCaller(Class<?> specialCaller) { void checkSpecialCaller(Class<?> specialCaller) throws NoAccessException {
if (allowedModes == TRUSTED) return; if (allowedModes == TRUSTED) return;
if (!VerifyAccess.isSamePackageMember(specialCaller, lookupClass())) if (!VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))
throw newNoAccessException("no private access for invokespecial", throw newNoAccessException("no private access for invokespecial",
new MemberName(specialCaller), lookupClass()); new MemberName(specialCaller), lookupClass());
} }
MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) { MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws NoAccessException {
// The accessing class only has the right to use a protected member // The accessing class only has the right to use a protected member
// on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc. // on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc.
if (!method.isProtected() || method.isStatic() if (!method.isProtected() || method.isStatic()
...@@ -712,7 +730,7 @@ public class MethodHandles { ...@@ -712,7 +730,7 @@ public class MethodHandles {
else else
return restrictReceiver(method, mh, lookupClass()); return restrictReceiver(method, mh, lookupClass());
} }
MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) { MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws NoAccessException {
assert(!method.isStatic()); assert(!method.isStatic());
Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide
if (defc.isInterface() || !defc.isAssignableFrom(caller)) { if (defc.isInterface() || !defc.isAssignableFrom(caller)) {
...@@ -898,11 +916,16 @@ public class MethodHandles { ...@@ -898,11 +916,16 @@ public class MethodHandles {
* @return a method handle which always invokes the call site's target * @return a method handle which always invokes the call site's target
*/ */
public static public static
MethodHandle dynamicInvoker(CallSite site) { MethodHandle dynamicInvoker(CallSite site) throws NoAccessException {
MethodHandle getCSTarget = GET_TARGET; MethodHandle getCSTarget = GET_TARGET;
if (getCSTarget == null) if (getCSTarget == null) {
GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP. try {
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
} catch (NoAccessException ex) {
throw new InternalError();
}
}
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, getCSTarget, site); MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, getCSTarget, site);
MethodHandle invoker = exactInvoker(site.type()); MethodHandle invoker = exactInvoker(site.type());
return foldArguments(invoker, getTarget); return foldArguments(invoker, getTarget);
...@@ -1260,17 +1283,20 @@ public class MethodHandles { ...@@ -1260,17 +1283,20 @@ public class MethodHandles {
* <p> * <p>
* <b>Example:</b> * <b>Example:</b>
* <p><blockquote><pre> * <p><blockquote><pre>
* MethodHandle cat = MethodHandles.lookup(). * import static java.dyn.MethodHandles.*;
* findVirtual(String.class, "concat", String.class, String.class); * import static java.dyn.MethodType.*;
* System.out.println(cat.&lt;String&gt;invokeExact("x", "y")); // xy * ...
* MethodHandle cat = lookup().findVirtual(String.class,
* "concat", methodType(String.class, String.class));
* System.out.println((String) cat.invokeExact("x", "y")); // xy
* MethodHandle d0 = dropArguments(cat, 0, String.class); * MethodHandle d0 = dropArguments(cat, 0, String.class);
* System.out.println(d0.&lt;String&gt;invokeExact("x", "y", "z")); // xy * System.out.println((String) d0.invokeExact("x", "y", "z")); // yz
* MethodHandle d1 = dropArguments(cat, 1, String.class); * MethodHandle d1 = dropArguments(cat, 1, String.class);
* System.out.println(d1.&lt;String&gt;invokeExact("x", "y", "z")); // xz * System.out.println((String) d1.invokeExact("x", "y", "z")); // xz
* MethodHandle d2 = dropArguments(cat, 2, String.class); * MethodHandle d2 = dropArguments(cat, 2, String.class);
* System.out.println(d2.&lt;String&gt;invokeExact("x", "y", "z")); // yz * System.out.println((String) d2.invokeExact("x", "y", "z")); // xy
* MethodHandle d12 = dropArguments(cat, 1, String.class, String.class); * MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
* System.out.println(d12.&lt;String&gt;invokeExact("w", "x", "y", "z")); // wz * System.out.println((String) d12.invokeExact("x", 12, true, "z")); // xz
* </pre></blockquote> * </pre></blockquote>
* @param target the method handle to invoke after the argument is dropped * @param target the method handle to invoke after the argument is dropped
* @param valueTypes the type(s) of the argument to drop * @param valueTypes the type(s) of the argument to drop
...@@ -1562,4 +1588,107 @@ public class MethodHandles { ...@@ -1562,4 +1588,107 @@ public class MethodHandles {
MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) { MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
return MethodHandleImpl.throwException(IMPL_TOKEN, MethodType.methodType(returnType, exType)); return MethodHandleImpl.throwException(IMPL_TOKEN, MethodType.methodType(returnType, exType));
} }
/**
* Produce a wrapper instance of the given "SAM" type which redirects its calls to the given method handle.
* A SAM type is a type which declares a single abstract method.
* Additionally, it must have either no constructor (as an interface)
* or have a public or protected constructor of zero arguments (as a class).
* <p>
* The resulting instance of the required SAM type will respond to
* invocation of the SAM type's single abstract method by calling
* 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.invokeExact}.
* <p>
* The method handle may throw an <em>undeclared exception</em>,
* which means any checked exception (or other checked throwable)
* not declared by the SAM type's single abstract method.
* If this happens, the throwable will be wrapped in an instance
* of {@link UndeclaredThrowableException} and thrown in that
* wrapped form.
* <p>
* The wrapper instance is guaranteed to be of a non-public
* implementation class C in a package containing no classes
* or methods except system-defined classes and methods.
* The implementation class C will have no public supertypes
* or public methods beyond the following:
* <ul>
* <li>the SAM type itself and any methods in the SAM type
* <li>the supertypes of the SAM type (if any) and their methods
* <li>{@link Object} and its methods
* <li>{@link MethodHandleProvider} and its methods
* </ul>
* <p>
* No stable mapping is promised between the SAM type and
* the implementation class C. Over time, several implementation
* classes might be used for the same SAM type.
* <p>
* This method is not guaranteed to return a distinct
* wrapper object for each separate call. If the JVM is able
* to prove that a wrapper has already been created for a given
* method handle, or for another method handle with the
* same behavior, the JVM may return that wrapper in place of
* a new wrapper.
* @param target the method handle to invoke from the wrapper
* @param samType the desired type of the wrapper, a SAM type
* @return a correctly-typed wrapper for the given {@code target}
* @throws IllegalArgumentException if the {@code target} throws
* an undeclared exception
*/
// ISSUE: Should we delegate equals/hashCode to the targets?
// Not useful unless there is a stable equals/hashCode behavior
// for MethodHandle, and for MethodHandleProvider.asMethodHandle.
public static
<T> T asInstance(MethodHandle target, Class<T> samType) {
// POC implementation only; violates the above contract several ways
final Method sam = getSamMethod(samType);
if (sam == null)
throw new IllegalArgumentException("not a SAM type: "+samType.getName());
MethodType samMT = MethodType.methodType(sam.getReturnType(), sam.getParameterTypes());
if (!samMT.equals(target.type()))
throw new IllegalArgumentException("wrong method type");
final MethodHandle mh = target;
return samType.cast(Proxy.newProxyInstance(
samType.getClassLoader(),
new Class[]{ samType, MethodHandleProvider.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == MethodHandleProvider.class) {
return method.invoke(mh, args);
}
assert method.equals(sam) : method;
return mh.invokeVarargs(args);
}
}));
}
private static
Method getSamMethod(Class<?> samType) {
Method sam = null;
for (Method m : samType.getMethods()) {
int mod = m.getModifiers();
if (Modifier.isAbstract(mod)) {
if (sam != null)
return null; // too many abstract methods
sam = m;
}
}
if (!samType.isInterface() && getSamConstructor(samType) == null)
return null; // wrong kind of constructor
return sam;
}
private static
Constructor getSamConstructor(Class<?> samType) {
for (Constructor c : samType.getDeclaredConstructors()) {
if (c.getParameterTypes().length == 0) {
int mod = c.getModifiers();
if (Modifier.isPublic(mod) || Modifier.isProtected(mod))
return c;
}
}
return null;
}
} }
...@@ -40,24 +40,37 @@ import static sun.dyn.MemberName.newIllegalArgumentException; ...@@ -40,24 +40,37 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
* returned by a method handle, or the arguments and return type passed * returned by a method handle, or the arguments and return type passed
* and expected by a method handle caller. Method types must be properly * and expected by a method handle caller. Method types must be properly
* matched between a method handle and all its callers, * matched between a method handle and all its callers,
* and the JVM's operations enforce this matching at all times. * and the JVM's operations enforce this matching at, specifically
* during calls to {@link MethodHandle#invokeExact}
* and {@link MethodHandle#invokeGeneric}, and during execution
* of {@code invokedynamic} instructions.
* <p> * <p>
* The structure is a return type accompanied by any number of parameter types. * The structure is a return type accompanied by any number of parameter types.
* The types (primitive, void, and reference) are represented by Class objects. * The types (primitive, {@code void}, and reference) are represented by {@link Class} objects.
* (For ease of exposition, we treat {@code void} as if it were a type.
* In fact, it denotes the absence of a return type.)
* <p> * <p>
* All instances of <code>MethodType</code> are immutable. * All instances of {@code MethodType} are immutable.
* Two instances are completely interchangeable if they compare equal. * Two instances are completely interchangeable if they compare equal.
* Equality depends on pairwise correspondence of the return and parameter types and on nothing else. * Equality depends on pairwise correspondence of the return and parameter types and on nothing else.
* <p> * <p>
* This type can be created only by factory methods. * This type can be created only by factory methods.
* All factory methods may cache values, though caching is not guaranteed. * All factory methods may cache values, though caching is not guaranteed.
* <p> * <p>
* Note: Like classes and strings, method types can be represented directly * {@code MethodType} objects are sometimes derived from bytecode instructions
* as constants to be loaded by {@code ldc} bytecodes. * such as {@code invokedynamic}, specifically from the type descriptor strings associated
* with the instructions in a class file's constant pool.
* When this occurs, any classes named in the descriptor strings must be loaded.
* (But they need not be initialized.)
* This loading may occur at any time before the {@code MethodType} object is first derived.
* <p>
* Like classes and strings, method types can be represented directly
* in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
* Loading such a constant causes its component classes to be loaded as necessary.
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
public final public final
class MethodType { class MethodType implements java.lang.reflect.Type {
private final Class<?> rtype; private final Class<?> rtype;
private final Class<?>[] ptypes; private final Class<?>[] ptypes;
private MethodTypeForm form; // erased form, plus cached data about primitives private MethodTypeForm form; // erased form, plus cached data about primitives
...@@ -636,11 +649,11 @@ class MethodType { ...@@ -636,11 +649,11 @@ class MethodType {
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Find or create an instance of the given method type. * Find or create an instance of the given method type.
* Any class or interface name embedded in the signature string * Any class or interface name embedded in the descriptor string
* will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)} * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
* on the given loader (or if it is null, on the system class loader). * on the given loader (or if it is null, on the system class loader).
* <p> * <p>
* Note that it is possible to build method types which cannot be * Note that it is possible to encounter method types which cannot be
* constructed by this method, because their component types are * constructed by this method, because their component types are
* not all reachable from a common class loader. * not all reachable from a common class loader.
* <p> * <p>
...@@ -662,8 +675,11 @@ class MethodType { ...@@ -662,8 +675,11 @@ class MethodType {
} }
/** /**
* Create a bytecode signature representation of the type. * Create a bytecode descriptor representation of the method type.
* Note that this is not a strict inverse of * <p>
* Note that this is not a strict inverse of {@link #fromMethodDescriptorString}.
* Two distinct classes which share a common name but have different class loaders
* will appear identical when viewed within descriptor strings.
* <p> * <p>
* This method is included for the benfit of applications that must * This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic. * generate bytecodes that process method handles and invokedynamic.
......
...@@ -37,7 +37,7 @@ package java.dyn; ...@@ -37,7 +37,7 @@ package java.dyn;
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
* @since 1.7 * @since 1.7
*/ */
public class NoAccessException extends RuntimeException { public class NoAccessException extends ReflectiveOperationException {
private static final long serialVersionUID = 292L; private static final long serialVersionUID = 292L;
/** /**
......
...@@ -40,20 +40,18 @@ ...@@ -40,20 +40,18 @@
* The JVM links any such call (regardless of signature) to a dynamically * The JVM links any such call (regardless of signature) to a dynamically
* typed method handle invocation. In the case of {@code invokeGeneric}, * typed method handle invocation. In the case of {@code invokeGeneric},
* argument and return value conversions are applied. * argument and return value conversions are applied.
* </li>
* *
* <li>In source code, the class {@link java.dyn.InvokeDynamic} appears to accept * <li>In source code, the class {@link java.dyn.InvokeDynamic InvokeDynamic} appears to accept
* any static method invocation, of any name and any signature. * any static method invocation, of any name and any signature.
* But instead of emitting * But instead of emitting
* an {@code invokestatic} instruction for such a call, the Java compiler emits * an {@code invokestatic} instruction for such a call, the Java compiler emits
* an {@code invokedynamic} instruction with the given name and signature. * an {@code invokedynamic} instruction with the given name and signature.
* * </li>
* <li>When the JVM links an {@code invokedynamic} instruction, it calls the
* {@linkplain java.dyn.Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method}
* of the containing class to obtain a {@linkplain java.dyn.CallSite call site} object through which
* the call site will link its target {@linkplain java.dyn.MethodHandle method handle}.
* *
* <li>The JVM bytecode format supports immediate constants of * <li>The JVM bytecode format supports immediate constants of
* the classes {@link java.dyn.MethodHandle} and {@link java.dyn.MethodType}. * the classes {@link java.dyn.MethodHandle MethodHandle} and {@link java.dyn.MethodType MethodType}.
* </li>
* </ul> * </ul>
* *
* <h2><a name="jvm_mods"></a>Corresponding JVM bytecode format changes</h2> * <h2><a name="jvm_mods"></a>Corresponding JVM bytecode format changes</h2>
...@@ -65,18 +63,50 @@ ...@@ -65,18 +63,50 @@
* The first byte is the opcode 186 (hexadecimal {@code BA}). * The first byte is the opcode 186 (hexadecimal {@code BA}).
* The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions). * The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions).
* The final two bytes are reserved for future use and required to be zero. * The final two bytes are reserved for future use and required to be zero.
* The constant pool reference is to a entry with tag {@code CONSTANT_NameAndType} * The constant pool reference of an {@code invokedynamic} instruction is to a entry
* (decimal 12). It is thus not a method reference of any sort, but merely * with tag {@code CONSTANT_InvokeDynamic} (decimal 17). See below for its format.
* the method name, argument types, and return type of the dynamic call site. * The entry specifies the bootstrap method (a {@link java.dyn.MethodHandle MethodHandle} constant),
* <em>(TBD: The EG is discussing the possibility of a special constant pool entry type, * the dynamic invocation name, and the argument types and return type of the call.
* so that other information may be added, such as a per-instruction bootstrap * <p>
* method and/or annotations.)</em> * Each instance of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
* Multiple instances of an {@code invokedynamic} instruction can share a single
* {@code CONSTANT_InvokeDynamic} entry.
* In any case, distinct call sites always have distinct linkage state.
* <p>
* Moreover, for the purpose of distinguishing dynamic call sites,
* the JVM is allowed (but not required) to make internal copies
* of {@code invokedynamic} instructions, each one
* constituting a separate dynamic call site with its own linkage state.
* Such copying, if it occurs, cannot be observed except indirectly via
* execution of bootstrap methods and target methods.
* <p>
* A dynamic call site is originally in an unlinked state. In this state, there is
* no target method for the call site to invoke.
* A dynamic call site is linked by means of a bootstrap method,
* as <a href="#bsm">described below</a>.
* <p>
* <em>(Historic Note: Some older JVMs may allow the index of a {@code CONSTANT_NameAndType}
* instead of a {@code CONSTANT_InvokeDynamic}. In earlier, obsolete versions of this API, the
* bootstrap method was specified dynamically, in a per-class basis, during class initialization.)</em>
*
* <h3>constant pool entries for {@code invokedynamic} instructions</h3>
* If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 17),
* it must contain exactly four more bytes.
* The first two bytes after the tag must be an index to a {@code CONSTANT_MethodHandle}
* entry, and the second two bytes must be an index to a {@code CONSTANT_NameAndType}.
* The first index specifies a bootstrap method used by the associated dynamic call sites.
* The second index specifies the method name, argument types, and return type of the dynamic call site.
* The structure of such an entry is therefore analogous to a {@code CONSTANT_Methodref},
* except that the {@code CONSTANT_Class} reference in a {@code CONSTANT_Methodref} entry
* is replaced by a bootstrap method reference.
* *
* <h3>constant pool entries for {@code MethodType}s</h3> * <h3>constant pool entries for {@code MethodType}s</h3>
* If a constant pool entry has the tag {@code CONSTANT_MethodType} (decimal 16), * If a constant pool entry has the tag {@code CONSTANT_MethodType} (decimal 16),
* it must contain exactly two more bytes, which are an index to a {@code CONSTANT_Utf8} * it must contain exactly two more bytes, which must be an index to a {@code CONSTANT_Utf8}
* entry which represents a method type signature. The JVM will ensure that on first * entry which represents a method type signature.
* execution of an {@code ldc} instruction for this entry, a {@link java.dyn.MethodType} * <p>
* The JVM will ensure that on first
* execution of an {@code ldc} instruction for this entry, a {@link java.dyn.MethodType MethodType}
* will be created which represents the signature. * will be created which represents the signature.
* Any classes mentioned in the {@code MethodType} will be loaded if necessary, * Any classes mentioned in the {@code MethodType} will be loaded if necessary,
* but not initialized. * but not initialized.
...@@ -86,12 +116,15 @@ ...@@ -86,12 +116,15 @@
* <h3>constant pool entries for {@code MethodHandle}s</h3> * <h3>constant pool entries for {@code MethodHandle}s</h3>
* If a constant pool entry has the tag {@code CONSTANT_MethodHandle} (decimal 15), * If a constant pool entry has the tag {@code CONSTANT_MethodHandle} (decimal 15),
* it must contain exactly three more bytes. The first byte after the tag is a subtag * it must contain exactly three more bytes. The first byte after the tag is a subtag
* value in the range 1 through 9, and the last two are an index to a * value which must be in the range 1 through 9, and the last two must be an index to a
* {@code CONSTANT_Fieldref}, {@code CONSTANT_Methodref}, or * {@code CONSTANT_Fieldref}, {@code CONSTANT_Methodref}, or
* {@code CONSTANT_InterfaceMethodref} entry which represents a field or method * {@code CONSTANT_InterfaceMethodref} entry which represents a field or method
* for which a method handle is to be created. * for which a method handle is to be created.
* Furthermore, the subtag value and the type of the constant index value
* must agree according to the table below.
* <p>
* The JVM will ensure that on first execution of an {@code ldc} instruction * The JVM will ensure that on first execution of an {@code ldc} instruction
* for this entry, a {@link java.dyn.MethodHandle} will be created which represents * for this entry, a {@link java.dyn.MethodHandle MethodHandle} will be created which represents
* the field or method reference, according to the specific mode implied by the subtag. * the field or method reference, according to the specific mode implied by the subtag.
* <p> * <p>
* As with {@code CONSTANT_Class} and {@code CONSTANT_MethodType} constants, * As with {@code CONSTANT_Class} and {@code CONSTANT_MethodType} constants,
...@@ -126,6 +159,129 @@ ...@@ -126,6 +159,129 @@
* Method handles for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic} * Method handles for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic}
* may force class initialization on their first invocation, just like the corresponding bytecodes. * may force class initialization on their first invocation, just like the corresponding bytecodes.
* *
* <h2><a name="bsm"></a>Bootstrap Methods</h2>
* Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
* the call site must first be <em>linked</em>.
* Linking is accomplished by calling a <em>bootstrap method</em>
* which is given the static information content of the call site,
* and which must produce a {@link java.dyn.MethodHandle method handle}
* that gives the behavior of the call site.
* <p>
* Each {@code invokedynamic} instruction statically specifies its own
* bootstrap method as a constant pool reference.
* The constant pool reference also specifies the call site's name and type signature,
* just like {@code invokevirtual} and the other invoke instructions.
* <p>
* Linking starts with resolving the constant pool entry for the
* bootstrap method, and resolving a {@link java.dyn.MethodType MethodType} object for
* the type signature of the dynamic call site.
* This resolution process may trigger class loading.
* It may therefore throw an error if a class fails to load.
* This error becomes the abnormal termination of the dynamic
* call site execution.
* Linkage does not trigger class initialization.
* <p>
* Next, the bootstrap method call is started, with four values being stacked:
* <ul>
* <li>a {@code MethodHandle}, the resolved bootstrap method itself </li>
* <li>a {@code Class}, the <em>caller class</em> in which dynamic call site occurs </li>
* <li>a {@code String}, the method name mentioned in the call site </li>
* <li>a {@code MethodType}, the resolved type signature of the call </li>
* </ul>
* The method handle is then applied to the other values as if by
* {@linkplain java.dyn.MethodHandle#invokeGeneric the <code>invokeGeneric</code> method}.
* The returned result must be a {@link java.dyn.CallSite CallSite}, a {@link java.dyn.MethodHandle MethodHandle},
* or another {@link java.dyn.MethodHandleProvider MethodHandleProvider} value.
* The method {@linkplain java.dyn.MethodHandleProvider#asMethodHandle asMethodHandle}
* is then called on the returned value. The result of that second
* call is the {@code MethodHandle} which becomes the
* permanent binding for the dynamic call site.
* That method handle's type must be exactly equal to the type
* derived from the dynamic call site signature and passed to
* the bootstrap method.
* <p>
* After resolution, the linkage process may fail in a variety of ways.
* All failures are reported by an {@link java.dyn.InvokeDynamicBootstrapError InvokeDynamicBootstrapError},
* which is thrown as the abnormal termination of the dynamic call
* site execution.
* The following circumstances will cause this:
* <ul>
* <li>the bootstrap method invocation completes abnormally </li>
* <li>the result from the bootstrap invocation is not a reference to
* an object of type {@link java.dyn.MethodHandleProvider MethodHandleProvider} </li>
* <li>the call to {@code asMethodHandle} completes abnormally </li>
* <li>the call to {@code asMethodHandle} fails to return a reference to
* an object of type {@link java.dyn.MethodHandle MethodHandle} </li>
* <li>the method handle produced by {@code asMethodHandle} does not have
* the expected {@code MethodType} </li>
* </ul>
* <h3>timing of linkage</h3>
* A dynamic call site is linked just before its first execution.
* The bootstrap method call implementing the linkage occurs within
* a thread that is attempting a first execution.
* <p>
* If there are several such threads, the JVM picks one thread
* and runs the bootstrap method while the others wait for the
* invocation to terminate normally or abnormally.
* <p>
* After a bootstrap method is called and a method handle target
* successfully extracted, the JVM attempts to link the instruction
* being executed to the target method handle.
* This may fail if there has been intervening linkage
* or invalidation event for the same instruction.
* If such a failure occurs, the dynamic call site must be
* re-executed from the beginning, either re-linking it
* (if it has been invalidated) or invoking the target
* (if it the instruction has been linked by some other means).
* <p>
* If the instruction is linked successfully, the target method
* handle is invoked to complete the instruction execution.
* The state of linkage continues until the method containing the
* dynamic call site is garbage collected, or the dynamic call site
* is invalidated by an explicit request,
* such as {@link java.dyn.Linkage#invalidateCallerClass Linkage.invalidateCallerClass}.
* <p>
* In an application which requires dynamic call sites with individually
* mutable behaviors, their bootstrap methods should produce distinct
* {@link java.dyn.CallSite CallSite} objects, one for each linkage request.
* <p>
* If a class containing {@code invokedynamic} instructions
* is {@linkplain java.dyn.Linkage#invalidateCallerClass(Class) invalidated},
* subsequent execution of those {@code invokedynamic} instructions
* will require linking.
* It is as if they had never been executed in the first place.
* (However, invalidation does not cause constant pool entries to be
* resolved a second time.)
* <p>
* Invalidation events and bootstrap method calls for a particular
* dynamic call site are globally ordered relative to each other.
* When an invokedynamic instruction is invalidated, if there is
* simultaneously a bootstrap method invocation in process
* (in the same thread or a different thread), the result
* eventually returned must not be used to link the call site.
* Put another way, when a call site is invalidated, its
* subsequent linkage (if any) must be performed by a bootstrap method
* call initiated after the invalidation occurred.
* <p>
* If several threads simultaneously execute a bootstrap method for a single dynamic
* call site, the JVM must choose one target object and installs it visibly to
* all threads. Any other bootstrap method calls are allowed to complete, but their
* results are ignored, and their dynamic call site invocations proceed with the originally
* chosen target object.
* <p>
* The JVM is free to duplicate dynamic call sites.
* This means that, even if a class contains just one {@code invokedynamic}
* instruction, its bootstrap method may be executed several times,
* once for each duplicate. Thus, bootstrap method code should not
* assume an exclusive one-to-one correspondence between particular occurrences
* of {@code invokedynamic} bytecodes in class files and linkage events.
* <p>
* In principle, each individual execution of an {@code invokedynamic}
* instruction could be deemed (by a conforming implementation) to be a separate
* duplicate, requiring its own execution of the bootstrap method.
* However, implementations are expected to perform code duplication
* (if at all) in order to improve performance, not make it worse.
*
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
......
...@@ -44,11 +44,12 @@ import java.io.Serializable; ...@@ -44,11 +44,12 @@ import java.io.Serializable;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.text.spi.DateFormatSymbolsProvider; import java.text.spi.DateFormatSymbolsProvider;
import java.util.Arrays; import java.util.Arrays;
import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.LocaleServiceProvider; import java.util.spi.LocaleServiceProvider;
import sun.util.LocaleServiceProviderPool; import sun.util.LocaleServiceProviderPool;
import sun.util.TimeZoneNameUtility; import sun.util.TimeZoneNameUtility;
...@@ -321,20 +322,64 @@ public class DateFormatSymbols implements Serializable, Cloneable { ...@@ -321,20 +322,64 @@ public class DateFormatSymbols implements Serializable, Cloneable {
* @since 1.6 * @since 1.6
*/ */
public static final DateFormatSymbols getInstance(Locale locale) { public static final DateFormatSymbols getInstance(Locale locale) {
DateFormatSymbols dfs = getProviderInstance(locale);
if (dfs != null) {
return dfs;
}
return (DateFormatSymbols) getCachedInstance(locale).clone();
}
/**
* Returns a DateFormatSymbols provided by a provider or found in
* the cache. Note that this method returns a cached instance,
* not its clone. Therefore, the instance should never be given to
* an application.
*/
static final DateFormatSymbols getInstanceRef(Locale locale) {
DateFormatSymbols dfs = getProviderInstance(locale);
if (dfs != null) {
return dfs;
}
return getCachedInstance(locale);
}
private static DateFormatSymbols getProviderInstance(Locale locale) {
DateFormatSymbols providersInstance = null;
// Check whether a provider can provide an implementation that's closer // Check whether a provider can provide an implementation that's closer
// to the requested locale than what the Java runtime itself can provide. // to the requested locale than what the Java runtime itself can provide.
LocaleServiceProviderPool pool = LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class); LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
if (pool.hasProviders()) { if (pool.hasProviders()) {
DateFormatSymbols providersInstance = pool.getLocalizedObject( providersInstance = pool.getLocalizedObject(
DateFormatSymbolsGetter.INSTANCE, locale); DateFormatSymbolsGetter.INSTANCE, locale);
if (providersInstance != null) { }
return providersInstance; return providersInstance;
}
/**
* Returns a cached DateFormatSymbols if it's found in the
* cache. Otherwise, this method returns a newly cached instance
* for the given locale.
*/
private static DateFormatSymbols getCachedInstance(Locale locale) {
SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
DateFormatSymbols dfs = null;
if (ref == null || (dfs = ref.get()) == null) {
dfs = new DateFormatSymbols(locale);
ref = new SoftReference<DateFormatSymbols>(dfs);
SoftReference<DateFormatSymbols> x = cachedInstances.putIfAbsent(locale, ref);
if (x != null) {
DateFormatSymbols y = x.get();
if (y != null) {
dfs = y;
} else {
// Replace the empty SoftReference with ref.
cachedInstances.put(locale, ref);
}
} }
} }
return dfs;
return new DateFormatSymbols(locale);
} }
/** /**
...@@ -597,56 +642,44 @@ public class DateFormatSymbols implements Serializable, Cloneable { ...@@ -597,56 +642,44 @@ public class DateFormatSymbols implements Serializable, Cloneable {
static final int millisPerHour = 60*60*1000; static final int millisPerHour = 60*60*1000;
/** /**
* Cache to hold the FormatData and TimeZoneNames ResourceBundles * Cache to hold DateFormatSymbols instances per Locale.
* of a Locale.
*/ */
private static Hashtable cachedLocaleData = new Hashtable(3); private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
= new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(3);
/** private void initializeData(Locale desiredLocale) {
* Look up resource data for the desiredLocale in the cache; update the locale = desiredLocale;
* cache if necessary.
*/ // Copy values of a cached instance if any.
private static ResourceBundle cacheLookup(Locale desiredLocale) { SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
ResourceBundle rb; DateFormatSymbols dfs;
SoftReference data if (ref != null && (dfs = ref.get()) != null) {
= (SoftReference)cachedLocaleData.get(desiredLocale); copyMembers(dfs, this);
if (data == null) { return;
rb = LocaleData.getDateFormatData(desiredLocale);
data = new SoftReference(rb);
cachedLocaleData.put(desiredLocale, data);
} else {
if ((rb = (ResourceBundle)data.get()) == null) {
rb = LocaleData.getDateFormatData(desiredLocale);
data = new SoftReference(rb);
}
} }
return rb;
}
private void initializeData(Locale desiredLocale) { // Initialize the fields from the ResourceBundle for locale.
int i; ResourceBundle resource = LocaleData.getDateFormatData(locale);
ResourceBundle resource = cacheLookup(desiredLocale);
// FIXME: cache only ResourceBundle. Hence every time, will do eras = resource.getStringArray("Eras");
// getObject(). This won't be necessary if the Resource itself
// is cached.
eras = (String[])resource.getObject("Eras");
months = resource.getStringArray("MonthNames"); months = resource.getStringArray("MonthNames");
shortMonths = resource.getStringArray("MonthAbbreviations"); shortMonths = resource.getStringArray("MonthAbbreviations");
String[] lWeekdays = resource.getStringArray("DayNames");
weekdays = new String[8];
weekdays[0] = ""; // 1-based
for (i=0; i<lWeekdays.length; i++)
weekdays[i+1] = lWeekdays[i];
String[] sWeekdays = resource.getStringArray("DayAbbreviations");
shortWeekdays = new String[8];
shortWeekdays[0] = ""; // 1-based
for (i=0; i<sWeekdays.length; i++)
shortWeekdays[i+1] = sWeekdays[i];
ampms = resource.getStringArray("AmPmMarkers"); ampms = resource.getStringArray("AmPmMarkers");
localPatternChars = resource.getString("DateTimePatternChars"); localPatternChars = resource.getString("DateTimePatternChars");
locale = desiredLocale; // Day of week names are stored in a 1-based array.
weekdays = toOneBasedArray(resource.getStringArray("DayNames"));
shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations"));
}
private static String[] toOneBasedArray(String[] src) {
int len = src.length;
String[] dst = new String[len + 1];
dst[0] = "";
for (int i = 0; i < len; i++) {
dst[i + 1] = src[i];
}
return dst;
} }
/** /**
......
/* /*
* Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -46,9 +46,10 @@ import java.math.BigInteger; ...@@ -46,9 +46,10 @@ import java.math.BigInteger;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Currency; import java.util.Currency;
import java.util.Hashtable;
import java.util.Locale; import java.util.Locale;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import sun.util.resources.LocaleData; import sun.util.resources.LocaleData;
...@@ -394,14 +395,14 @@ public class DecimalFormat extends NumberFormat { ...@@ -394,14 +395,14 @@ public class DecimalFormat extends NumberFormat {
public DecimalFormat() { public DecimalFormat() {
Locale def = Locale.getDefault(Locale.Category.FORMAT); Locale def = Locale.getDefault(Locale.Category.FORMAT);
// try to get the pattern from the cache // try to get the pattern from the cache
String pattern = (String) cachedLocaleData.get(def); String pattern = cachedLocaleData.get(def);
if (pattern == null) { /* cache miss */ if (pattern == null) { /* cache miss */
// Get the pattern for the default locale. // Get the pattern for the default locale.
ResourceBundle rb = LocaleData.getNumberFormatData(def); ResourceBundle rb = LocaleData.getNumberFormatData(def);
String[] all = rb.getStringArray("NumberPatterns"); String[] all = rb.getStringArray("NumberPatterns");
pattern = all[0]; pattern = all[0];
/* update cache */ /* update cache */
cachedLocaleData.put(def, pattern); cachedLocaleData.putIfAbsent(def, pattern);
} }
// Always applyPattern after the symbols are set // Always applyPattern after the symbols are set
...@@ -3272,5 +3273,6 @@ public class DecimalFormat extends NumberFormat { ...@@ -3272,5 +3273,6 @@ public class DecimalFormat extends NumberFormat {
/** /**
* Cache to hold the NumberPattern of a Locale. * Cache to hold the NumberPattern of a Locale.
*/ */
private static Hashtable cachedLocaleData = new Hashtable(3); private static final ConcurrentMap<Locale, String> cachedLocaleData
= new ConcurrentHashMap<Locale, String>(3);
} }
...@@ -44,13 +44,14 @@ import java.io.ObjectInputStream; ...@@ -44,13 +44,14 @@ import java.io.ObjectInputStream;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.MissingResourceException; import java.util.MissingResourceException;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.SimpleTimeZone; import java.util.SimpleTimeZone;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import sun.util.calendar.CalendarUtils; import sun.util.calendar.CalendarUtils;
import sun.util.calendar.ZoneInfoFile; import sun.util.calendar.ZoneInfoFile;
import sun.util.resources.LocaleData; import sun.util.resources.LocaleData;
...@@ -503,14 +504,14 @@ public class SimpleDateFormat extends DateFormat { ...@@ -503,14 +504,14 @@ public class SimpleDateFormat extends DateFormat {
/** /**
* Cache to hold the DateTimePatterns of a Locale. * Cache to hold the DateTimePatterns of a Locale.
*/ */
private static Hashtable<String,String[]> cachedLocaleData private static final ConcurrentMap<String, String[]> cachedLocaleData
= new Hashtable<String,String[]>(3); = new ConcurrentHashMap<String, String[]>(3);
/** /**
* Cache NumberFormat instances with Locale key. * Cache NumberFormat instances with Locale key.
*/ */
private static Hashtable<Locale,NumberFormat> cachedNumberFormatData private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
= new Hashtable<Locale,NumberFormat>(3); = new ConcurrentHashMap<Locale, NumberFormat>(3);
/** /**
* The Locale used to instantiate this * The Locale used to instantiate this
...@@ -579,7 +580,7 @@ public class SimpleDateFormat extends DateFormat { ...@@ -579,7 +580,7 @@ public class SimpleDateFormat extends DateFormat {
initializeCalendar(locale); initializeCalendar(locale);
this.pattern = pattern; this.pattern = pattern;
this.formatData = DateFormatSymbols.getInstance(locale); this.formatData = DateFormatSymbols.getInstanceRef(locale);
this.locale = locale; this.locale = locale;
initialize(locale); initialize(locale);
} }
...@@ -632,9 +633,9 @@ public class SimpleDateFormat extends DateFormat { ...@@ -632,9 +633,9 @@ public class SimpleDateFormat extends DateFormat {
dateTimePatterns = r.getStringArray("DateTimePatterns"); dateTimePatterns = r.getStringArray("DateTimePatterns");
} }
/* update cache */ /* update cache */
cachedLocaleData.put(key, dateTimePatterns); cachedLocaleData.putIfAbsent(key, dateTimePatterns);
} }
formatData = DateFormatSymbols.getInstance(loc); formatData = DateFormatSymbols.getInstanceRef(loc);
if ((timeStyle >= 0) && (dateStyle >= 0)) { if ((timeStyle >= 0) && (dateStyle >= 0)) {
Object[] dateTimeArgs = {dateTimePatterns[timeStyle], Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
dateTimePatterns[dateStyle + 4]}; dateTimePatterns[dateStyle + 4]};
...@@ -665,7 +666,7 @@ public class SimpleDateFormat extends DateFormat { ...@@ -665,7 +666,7 @@ public class SimpleDateFormat extends DateFormat {
numberFormat.setGroupingUsed(false); numberFormat.setGroupingUsed(false);
/* update cache */ /* update cache */
cachedNumberFormatData.put(loc, numberFormat); cachedNumberFormatData.putIfAbsent(loc, numberFormat);
} }
numberFormat = (NumberFormat) numberFormat.clone(); numberFormat = (NumberFormat) numberFormat.clone();
...@@ -897,7 +898,7 @@ public class SimpleDateFormat extends DateFormat { ...@@ -897,7 +898,7 @@ public class SimpleDateFormat extends DateFormat {
* so we can call it from readObject(). * so we can call it from readObject().
*/ */
private void initializeDefaultCentury() { private void initializeDefaultCentury() {
calendar.setTime( new Date() ); calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add( Calendar.YEAR, -80 ); calendar.add( Calendar.YEAR, -80 );
parseAmbiguousDatesAsAfter(calendar.getTime()); parseAmbiguousDatesAsAfter(calendar.getTime());
} }
...@@ -921,7 +922,7 @@ public class SimpleDateFormat extends DateFormat { ...@@ -921,7 +922,7 @@ public class SimpleDateFormat extends DateFormat {
* @since 1.2 * @since 1.2
*/ */
public void set2DigitYearStart(Date startDate) { public void set2DigitYearStart(Date startDate) {
parseAmbiguousDatesAsAfter(startDate); parseAmbiguousDatesAsAfter(new Date(startDate.getTime()));
} }
/** /**
...@@ -934,7 +935,7 @@ public class SimpleDateFormat extends DateFormat { ...@@ -934,7 +935,7 @@ public class SimpleDateFormat extends DateFormat {
* @since 1.2 * @since 1.2
*/ */
public Date get2DigitYearStart() { public Date get2DigitYearStart() {
return defaultCenturyStart; return (Date) defaultCenturyStart.clone();
} }
/** /**
......
...@@ -51,6 +51,8 @@ import java.security.PrivilegedExceptionAction; ...@@ -51,6 +51,8 @@ import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import sun.util.BuddhistCalendar; import sun.util.BuddhistCalendar;
import sun.util.calendar.ZoneInfo; import sun.util.calendar.ZoneInfo;
import sun.util.resources.LocaleData; import sun.util.resources.LocaleData;
...@@ -837,7 +839,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca ...@@ -837,7 +839,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
* Cache to hold the firstDayOfWeek and minimalDaysInFirstWeek * Cache to hold the firstDayOfWeek and minimalDaysInFirstWeek
* of a Locale. * of a Locale.
*/ */
private static Hashtable<Locale, int[]> cachedLocaleData = new Hashtable<Locale, int[]>(3); private static final ConcurrentMap<Locale, int[]> cachedLocaleData
= new ConcurrentHashMap<Locale, int[]>(3);
// Special values of stamp[] // Special values of stamp[]
/** /**
...@@ -1022,7 +1025,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca ...@@ -1022,7 +1025,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
// returns a BuddhistCalendar instance. // returns a BuddhistCalendar instance.
if ("th".equals(aLocale.getLanguage()) if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) { && ("TH".equals(aLocale.getCountry()))) {
cal = new BuddhistCalendar(zone, aLocale); cal = new BuddhistCalendar(zone, aLocale);
} else { } else {
cal = new GregorianCalendar(zone, aLocale); cal = new GregorianCalendar(zone, aLocale);
} }
...@@ -2588,7 +2591,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca ...@@ -2588,7 +2591,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
data = new int[2]; data = new int[2];
data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek")); data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek"));
data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek")); data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek"));
cachedLocaleData.put(desiredLocale, data); cachedLocaleData.putIfAbsent(desiredLocale, data);
} }
firstDayOfWeek = data[0]; firstDayOfWeek = data[0];
minimalDaysInFirstWeek = data[1]; minimalDaysInFirstWeek = data[1];
......
...@@ -758,7 +758,7 @@ public final class Locale implements Cloneable, Serializable { ...@@ -758,7 +758,7 @@ public final class Locale implements Cloneable, Serializable {
} }
private static void initDefault() { private static void initDefault() {
String language, region, country, variant; String language, region, script, country, variant;
language = AccessController.doPrivileged( language = AccessController.doPrivileged(
new GetPropertyAction("user.language", "en")); new GetPropertyAction("user.language", "en"));
// for compatibility, check for old user.region property // for compatibility, check for old user.region property
...@@ -774,43 +774,41 @@ public final class Locale implements Cloneable, Serializable { ...@@ -774,43 +774,41 @@ public final class Locale implements Cloneable, Serializable {
country = region; country = region;
variant = ""; variant = "";
} }
script = "";
} else { } else {
script = AccessController.doPrivileged(
new GetPropertyAction("user.script", ""));
country = AccessController.doPrivileged( country = AccessController.doPrivileged(
new GetPropertyAction("user.country", "")); new GetPropertyAction("user.country", ""));
variant = AccessController.doPrivileged( variant = AccessController.doPrivileged(
new GetPropertyAction("user.variant", "")); new GetPropertyAction("user.variant", ""));
} }
defaultLocale = getInstance(language, country, variant); defaultLocale = getInstance(language, script, country, variant, null);
} }
private static void initDefault(Locale.Category category) { private static void initDefault(Locale.Category category) {
String language, region, country, variant; // make sure defaultLocale is initialized
if (defaultLocale == null) {
initDefault();
}
Locale defaultCategoryLocale = getInstance(
AccessController.doPrivileged(
new GetPropertyAction(category.languageKey, defaultLocale.getLanguage())),
AccessController.doPrivileged(
new GetPropertyAction(category.scriptKey, defaultLocale.getScript())),
AccessController.doPrivileged(
new GetPropertyAction(category.countryKey, defaultLocale.getCountry())),
AccessController.doPrivileged(
new GetPropertyAction(category.variantKey, defaultLocale.getVariant())),
null);
switch (category) { switch (category) {
case DISPLAY: case DISPLAY:
language = AccessController.doPrivileged( defaultDisplayLocale = defaultCategoryLocale;
new GetPropertyAction("user.language.display", ""));
if ("".equals(language)) {
defaultDisplayLocale = getDefault();
} else {
country = AccessController.doPrivileged(
new GetPropertyAction("user.country.display", ""));
variant = AccessController.doPrivileged(
new GetPropertyAction("user.variant.display", ""));
defaultDisplayLocale = getInstance(language, country, variant);
}
break; break;
case FORMAT: case FORMAT:
language = AccessController.doPrivileged( defaultFormatLocale = defaultCategoryLocale;
new GetPropertyAction("user.language.format", ""));
if ("".equals(language)) {
defaultFormatLocale = getDefault();
} else {
country = AccessController.doPrivileged(
new GetPropertyAction("user.country.format", ""));
variant = AccessController.doPrivileged(
new GetPropertyAction("user.variant.format", ""));
defaultFormatLocale = getInstance(language, country, variant);
}
break; break;
} }
} }
...@@ -2124,13 +2122,31 @@ public final class Locale implements Cloneable, Serializable { ...@@ -2124,13 +2122,31 @@ public final class Locale implements Cloneable, Serializable {
* Category used to represent the default locale for * Category used to represent the default locale for
* displaying user interfaces. * displaying user interfaces.
*/ */
DISPLAY, DISPLAY("user.language.display",
"user.script.display",
"user.country.display",
"user.variant.display"),
/** /**
* Category used to represent the default locale for * Category used to represent the default locale for
* formatting dates, numbers, and/or currencies. * formatting dates, numbers, and/or currencies.
*/ */
FORMAT, FORMAT("user.language.format",
"user.script.format",
"user.country.format",
"user.variant.format");
Category(String languageKey, String scriptKey, String countryKey, String variantKey) {
this.languageKey = languageKey;
this.scriptKey = scriptKey;
this.countryKey = countryKey;
this.variantKey = variantKey;
}
final String languageKey;
final String scriptKey;
final String countryKey;
final String variantKey;
} }
/** /**
......
...@@ -160,11 +160,6 @@ abstract public class TimeZone implements Serializable, Cloneable { ...@@ -160,11 +160,6 @@ abstract public class TimeZone implements Serializable, Cloneable {
private static final int ONE_HOUR = 60*ONE_MINUTE; private static final int ONE_HOUR = 60*ONE_MINUTE;
private static final int ONE_DAY = 24*ONE_HOUR; private static final int ONE_DAY = 24*ONE_HOUR;
/**
* Cache to hold the SimpleDateFormat objects for a Locale.
*/
private static Hashtable cachedLocaleData = new Hashtable(3);
// Proclaim serialization compatibility with JDK 1.1 // Proclaim serialization compatibility with JDK 1.1
static final long serialVersionUID = 3581463369166924961L; static final long serialVersionUID = 3581463369166924961L;
......
...@@ -24,8 +24,10 @@ ...@@ -24,8 +24,10 @@
*/ */
package javax.swing; package javax.swing;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Font; import java.awt.Font;
import java.awt.Paint;
import javax.swing.border.*; import javax.swing.border.*;
/** /**
...@@ -636,4 +638,125 @@ public class BorderFactory ...@@ -636,4 +638,125 @@ public class BorderFactory
Icon tileIcon) { Icon tileIcon) {
return new MatteBorder(top, left, bottom, right, tileIcon); return new MatteBorder(top, left, bottom, right, tileIcon);
} }
//// StrokeBorder //////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* Creates a border of the specified {@code stroke}.
* The component's foreground color will be used to render the border.
*
* @param stroke the {@link BasicStroke} object used to stroke a shape
* @return the {@code Border} object
*
* @throws NullPointerException if the specified {@code stroke} is {@code null}
*
* @since 1.7
*/
public static Border createStrokeBorder(BasicStroke stroke) {
return new StrokeBorder(stroke);
}
/**
* Creates a border of the specified {@code stroke} and {@code paint}.
* If the specified {@code paint} is {@code null},
* the component's foreground color will be used to render the border.
*
* @param stroke the {@link BasicStroke} object used to stroke a shape
* @param paint the {@link Paint} object used to generate a color
* @return the {@code Border} object
*
* @throws NullPointerException if the specified {@code stroke} is {@code null}
*
* @since 1.7
*/
public static Border createStrokeBorder(BasicStroke stroke, Paint paint) {
return new StrokeBorder(stroke, paint);
}
//// DashedBorder //////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
private static Border sharedDashedBorder;
/**
* Creates a dashed border of the specified {@code paint}.
* If the specified {@code paint} is {@code null},
* the component's foreground color will be used to render the border.
* The width of a dash line is equal to {@code 1}.
* The relative length of a dash line and
* the relative spacing between dash lines are equal to {@code 1}.
* A dash line is not rounded.
*
* @param paint the {@link Paint} object used to generate a color
* @return the {@code Border} object
*
* @since 1.7
*/
public static Border createDashedBorder(Paint paint) {
return createDashedBorder(paint, 1.0f, 1.0f, 1.0f, false);
}
/**
* Creates a dashed border of the specified {@code paint},
* relative {@code length}, and relative {@code spacing}.
* If the specified {@code paint} is {@code null},
* the component's foreground color will be used to render the border.
* The width of a dash line is equal to {@code 1}.
* A dash line is not rounded.
*
* @param paint the {@link Paint} object used to generate a color
* @param length the relative length of a dash line
* @param spacing the relative spacing between dash lines
* @return the {@code Border} object
*
* @throws IllegalArgumentException if the specified {@code length} is less than {@code 1}, or
* if the specified {@code spacing} is less than {@code 0}
* @since 1.7
*/
public static Border createDashedBorder(Paint paint, float length, float spacing) {
return createDashedBorder(paint, 1.0f, length, spacing, false);
}
/**
* Creates a dashed border of the specified {@code paint}, {@code thickness},
* line shape, relative {@code length}, and relative {@code spacing}.
* If the specified {@code paint} is {@code null},
* the component's foreground color will be used to render the border.
*
* @param paint the {@link Paint} object used to generate a color
* @param thickness the width of a dash line
* @param length the relative length of a dash line
* @param spacing the relative spacing between dash lines
* @param rounded whether or not line ends should be round
* @return the {@code Border} object
*
* @throws IllegalArgumentException if the specified {@code thickness} is less than {@code 1}, or
* if the specified {@code length} is less than {@code 1}, or
* if the specified {@code spacing} is less than {@code 0}
* @since 1.7
*/
public static Border createDashedBorder(Paint paint, float thickness, float length, float spacing, boolean rounded) {
boolean shared = !rounded && (paint == null) && (thickness == 1.0f) && (length == 1.0f) && (spacing == 1.0f);
if (shared && (sharedDashedBorder != null)) {
return sharedDashedBorder;
}
if (thickness < 1.0f) {
throw new IllegalArgumentException("thickness is less than 1");
}
if (length < 1.0f) {
throw new IllegalArgumentException("length is less than 1");
}
if (spacing < 0.0f) {
throw new IllegalArgumentException("spacing is less than 0");
}
int cap = rounded ? BasicStroke.CAP_ROUND : BasicStroke.CAP_SQUARE;
int join = rounded ? BasicStroke.JOIN_ROUND : BasicStroke.JOIN_MITER;
float[] array = { thickness * (length - 1.0f), thickness * (spacing + 1.0f) };
Border border = createStrokeBorder(new BasicStroke(thickness, cap, join, thickness * 2.0f, array, 0.0f), paint);
if (shared) {
sharedDashedBorder = border;
}
return border;
}
} }
...@@ -1322,13 +1322,11 @@ public class DebugGraphics extends Graphics { ...@@ -1322,13 +1322,11 @@ public class DebugGraphics extends Graphics {
} }
String toShortString() { String toShortString() {
StringBuffer buffer = new StringBuffer("Graphics" + (isDrawingBuffer() ? "<B>" : "") + "(" + graphicsID + "-" + debugOptions + ")"); return "Graphics" + (isDrawingBuffer() ? "<B>" : "") + "(" + graphicsID + "-" + debugOptions + ")";
return buffer.toString();
} }
String pointToString(int x, int y) { String pointToString(int x, int y) {
StringBuffer buffer = new StringBuffer("(" + x + ", " + y + ")"); return "(" + x + ", " + y + ")";
return buffer.toString();
} }
/** Enables/disables diagnostic information about every graphics /** Enables/disables diagnostic information about every graphics
......
...@@ -4783,21 +4783,11 @@ public abstract class JComponent extends Container implements Serializable, ...@@ -4783,21 +4783,11 @@ public abstract class JComponent extends Container implements Serializable,
* @param y the y value of the dirty region * @param y the y value of the dirty region
* @param width the width of the dirty region * @param width the width of the dirty region
* @param height the height of the dirty region * @param height the height of the dirty region
* @see #isPaintingOrigin()
* @see java.awt.Component#isShowing * @see java.awt.Component#isShowing
* @see RepaintManager#addDirtyRegion * @see RepaintManager#addDirtyRegion
*/ */
public void repaint(long tm, int x, int y, int width, int height) { public void repaint(long tm, int x, int y, int width, int height) {
Container p = this;
while ((p = p.getParent()) instanceof JComponent) {
JComponent jp = (JComponent) p;
if (jp.isPaintingOrigin()) {
Rectangle rectangle = SwingUtilities.convertRectangle(
this, new Rectangle(x, y, width, height), jp);
jp.repaint(tm,
rectangle.x, rectangle.y, rectangle.width, rectangle.height);
return;
}
}
RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height); RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height);
} }
...@@ -4808,6 +4798,7 @@ public abstract class JComponent extends Container implements Serializable, ...@@ -4808,6 +4798,7 @@ public abstract class JComponent extends Container implements Serializable,
* currently pending events have been dispatched. * currently pending events have been dispatched.
* *
* @param r a <code>Rectangle</code> containing the dirty region * @param r a <code>Rectangle</code> containing the dirty region
* @see #isPaintingOrigin()
* @see java.awt.Component#isShowing * @see java.awt.Component#isShowing
* @see RepaintManager#addDirtyRegion * @see RepaintManager#addDirtyRegion
*/ */
...@@ -4912,13 +4903,19 @@ public abstract class JComponent extends Container implements Serializable, ...@@ -4912,13 +4903,19 @@ public abstract class JComponent extends Container implements Serializable,
} }
/** /**
* Returns true if a paint triggered on a child component should cause * Returns {@code true} if a paint triggered on a child component should cause
* painting to originate from this Component, or one of its ancestors. * painting to originate from this Component, or one of its ancestors.
* <p/>
* Calling {@link JComponent#repaint} on a Swing component will be delegated to
* the first ancestor which {@code isPaintingOrigin()} returns {@true},
* if there are any.
* <p/>
* {@code JComponent} subclasses that need to be repainted when any of their
* children are repainted should override this method to return {@code true}.
* *
* @return true if painting should originate from this Component or * @return always returns {@code false}
* one of its ancestors.
*/ */
boolean isPaintingOrigin() { protected boolean isPaintingOrigin() {
return false; return false;
} }
......
...@@ -303,10 +303,9 @@ public class JDialog extends Dialog implements WindowConstants, ...@@ -303,10 +303,9 @@ public class JDialog extends Dialog implements WindowConstants,
* @param modal specifies whether dialog blocks user input to other top-level * @param modal specifies whether dialog blocks user input to other top-level
* windows when shown. If {@code true}, the modality type property is set to * windows when shown. If {@code true}, the modality type property is set to
* {@code DEFAULT_MODALITY_TYPE}, otherwise the dialog is modeless. * {@code DEFAULT_MODALITY_TYPE}, otherwise the dialog is modeless.
* @param gc the {@code GraphicsConfiguration} * @param gc the {@code GraphicsConfiguration} of the target screen device;
* of the target screen device. If {@code gc} is * if {@code null}, the default system {@code GraphicsConfiguration}
* {@code null}, the same * is assumed
* {@code GraphicsConfiguration} as the owning Frame is used.
* @throws HeadlessException if {@code GraphicsEnvironment.isHeadless()} * @throws HeadlessException if {@code GraphicsEnvironment.isHeadless()}
* returns {@code true}. * returns {@code true}.
* @see java.awt.Dialog.ModalityType * @see java.awt.Dialog.ModalityType
...@@ -441,10 +440,9 @@ public class JDialog extends Dialog implements WindowConstants, ...@@ -441,10 +440,9 @@ public class JDialog extends Dialog implements WindowConstants,
* @param modal specifies whether dialog blocks user input to other top-level * @param modal specifies whether dialog blocks user input to other top-level
* windows when shown. If {@code true}, the modality type property is set to * windows when shown. If {@code true}, the modality type property is set to
* {@code DEFAULT_MODALITY_TYPE}, otherwise the dialog is modeless * {@code DEFAULT_MODALITY_TYPE}, otherwise the dialog is modeless
* @param gc the {@code GraphicsConfiguration} * @param gc the {@code GraphicsConfiguration} of the target screen device;
* of the target screen device. If {@code gc} is * if {@code null}, the default system {@code GraphicsConfiguration}
* {@code null}, the same * is assumed
* {@code GraphicsConfiguration} as the owning Dialog is used.
* @throws HeadlessException if {@code GraphicsEnvironment.isHeadless()} * @throws HeadlessException if {@code GraphicsEnvironment.isHeadless()}
* returns {@code true}. * returns {@code true}.
* @see java.awt.Dialog.ModalityType * @see java.awt.Dialog.ModalityType
...@@ -612,10 +610,8 @@ public class JDialog extends Dialog implements WindowConstants, ...@@ -612,10 +610,8 @@ public class JDialog extends Dialog implements WindowConstants,
* windows when shown. {@code null} value and unsupported modality * windows when shown. {@code null} value and unsupported modality
* types are equivalent to {@code MODELESS} * types are equivalent to {@code MODELESS}
* @param gc the {@code GraphicsConfiguration} of the target screen device; * @param gc the {@code GraphicsConfiguration} of the target screen device;
* if {@code null}, the {@code GraphicsConfiguration} from the owning * if {@code null}, the default system {@code GraphicsConfiguration}
* window is used; if {@code owner} is also {@code null}, the * is assumed
* system default {@code GraphicsConfiguration} is assumed
*
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if the {@code owner} is not an instance of {@link java.awt.Dialog Dialog} * if the {@code owner} is not an instance of {@link java.awt.Dialog Dialog}
* or {@link java.awt.Frame Frame} * or {@link java.awt.Frame Frame}
......
...@@ -384,7 +384,7 @@ public final class JLayer<V extends Component> ...@@ -384,7 +384,7 @@ public final class JLayer<V extends Component>
* @return true * @return true
* @see JComponent#isPaintingOrigin() * @see JComponent#isPaintingOrigin()
*/ */
boolean isPaintingOrigin() { protected boolean isPaintingOrigin() {
return true; return true;
} }
......
...@@ -637,14 +637,14 @@ public class JViewport extends JComponent implements Accessible ...@@ -637,14 +637,14 @@ public class JViewport extends JComponent implements Accessible
} }
/** /**
* Returns true if scroll mode is a BACKINGSTORE_SCROLL_MODE to cause * Returns true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE} to cause
* painting to originate from <code>JViewport</code>, or one of its * painting to originate from {@code JViewport}, or one of its
* ancestors. Otherwise returns false. * ancestors. Otherwise returns {@code false}.
* *
* @return true if if scroll mode is a BACKINGSTORE_SCROLL_MODE. * @return true if if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE}.
* @see JComponent#isPaintingOrigin() * @see JComponent#isPaintingOrigin()
*/ */
boolean isPaintingOrigin() { protected boolean isPaintingOrigin() {
return scrollMode == BACKINGSTORE_SCROLL_MODE; return scrollMode == BACKINGSTORE_SCROLL_MODE;
} }
......
...@@ -438,6 +438,7 @@ public class RepaintManager ...@@ -438,6 +438,7 @@ public class RepaintManager
* @param y Y coordinate of the region to repaint * @param y Y coordinate of the region to repaint
* @param w Width of the region to repaint * @param w Width of the region to repaint
* @param h Height of the region to repaint * @param h Height of the region to repaint
* @see JComponent#isPaintingOrigin()
* @see JComponent#repaint * @see JComponent#repaint
*/ */
public void addDirtyRegion(JComponent c, int x, int y, int w, int h) public void addDirtyRegion(JComponent c, int x, int y, int w, int h)
...@@ -447,6 +448,16 @@ public class RepaintManager ...@@ -447,6 +448,16 @@ public class RepaintManager
delegate.addDirtyRegion(c, x, y, w, h); delegate.addDirtyRegion(c, x, y, w, h);
return; return;
} }
Container p = c;
while ((p = p.getParent()) instanceof JComponent) {
JComponent jp = (JComponent) p;
if (jp.isPaintingOrigin()) {
Rectangle rectangle = SwingUtilities.convertRectangle(
c, new Rectangle(x, y, w, h), jp);
jp.repaint(0, rectangle.x, rectangle.y, rectangle.width, rectangle.height);
return;
}
}
addDirtyRegion0(c, x, y, w, h); addDirtyRegion0(c, x, y, w, h);
} }
......
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.border;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.beans.ConstructorProperties;
/**
* A class which implements a border of an arbitrary stroke.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI
* between applications running the same version of Swing.
* As of 1.4, support for long term storage of all JavaBeans&trade;
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Sergey A. Malenkov
*
* @since 1.7
*/
public class StrokeBorder extends AbstractBorder {
private final BasicStroke stroke;
private final Paint paint;
/**
* Creates a border of the specified {@code stroke}.
* The component's foreground color will be used to render the border.
*
* @param stroke the {@link BasicStroke} object used to stroke a shape
*
* @throws NullPointerException if the specified {@code stroke} is {@code null}
*/
public StrokeBorder(BasicStroke stroke) {
this(stroke, null);
}
/**
* Creates a border of the specified {@code stroke} and {@code paint}.
* If the specified {@code paint} is {@code null},
* the component's foreground color will be used to render the border.
*
* @param stroke the {@link BasicStroke} object used to stroke a shape
* @param paint the {@link Paint} object used to generate a color
*
* @throws NullPointerException if the specified {@code stroke} is {@code null}
*/
@ConstructorProperties({ "stroke", "paint" })
public StrokeBorder(BasicStroke stroke, Paint paint) {
if (stroke == null) {
throw new NullPointerException("border's stroke");
}
this.stroke = stroke;
this.paint = paint;
}
/**
* Paints the border for the specified component
* with the specified position and size.
*
* @param c the component for which this border is being painted
* @param g the paint graphics
* @param x the x position of the painted border
* @param y the y position of the painted border
* @param width the width of the painted border
* @param height the height of the painted border
*
* @throws NullPointerException if the specified {@code c} or {@code g} are {@code null}
*/
@Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
float size = this.stroke.getLineWidth();
if (size > 0.0f) {
g = g.create();
if (g instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(this.stroke);
g2d.setPaint(this.paint != null ? this.paint : c.getForeground());
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.draw(new Rectangle2D.Float(x + size / 2, y + size / 2, width - size, height - size));
}
g.dispose();
}
}
/**
* Reinitializes the {@code insets} parameter
* with this border's current insets.
* All insets are equal to the line width of the stroke.
*
* @param c the component for which this border insets value applies
* @param insets the {@code Insets} object to be reinitialized
* @return the reinitialized {@code insets} parameter
*
* @throws NullPointerException if the specified {@code insets} is {@code null}
*/
@Override
public Insets getBorderInsets(Component c, Insets insets) {
int size = (int) Math.ceil(this.stroke.getLineWidth());
insets.set(size, size, size, size);
return insets;
}
/**
* Returns the {@link BasicStroke} object used to stroke a shape
* during the border rendering.
*
* @return the {@link BasicStroke} object
*/
public BasicStroke getStroke() {
return this.stroke;
}
/**
* Returns the {@link Paint} object used to generate a color
* during the border rendering.
*
* @return the {@link Paint} object or {@code null}
* if the {@code paint} parameter is not set
*/
public Paint getPaint() {
return this.paint;
}
}
...@@ -362,6 +362,14 @@ public class BasicScrollPaneUI ...@@ -362,6 +362,14 @@ public class BasicScrollPaneUI
* @since 1.6 * @since 1.6
*/ */
public int getBaseline(JComponent c, int width, int height) { public int getBaseline(JComponent c, int width, int height) {
if (c == null) {
throw new NullPointerException("Component must be non-null");
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Width and height must be >= 0");
}
JViewport viewport = scrollpane.getViewport(); JViewport viewport = scrollpane.getViewport();
Insets spInsets = scrollpane.getInsets(); Insets spInsets = scrollpane.getInsets();
int y = spInsets.top; int y = spInsets.top;
......
...@@ -115,10 +115,7 @@ public class SynthTabbedPaneUI extends BasicTabbedPaneUI ...@@ -115,10 +115,7 @@ public class SynthTabbedPaneUI extends BasicTabbedPaneUI
return new SynthTabbedPaneUI(); return new SynthTabbedPaneUI();
} }
private SynthTabbedPaneUI() { private boolean scrollableTabLayoutEnabled() {
}
private boolean scrollableTabLayoutEnabled() {
return (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT); return (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT);
} }
......
...@@ -186,6 +186,9 @@ public class DefaultTableCellRenderer extends JLabel ...@@ -186,6 +186,9 @@ public class DefaultTableCellRenderer extends JLabel
*/ */
public Component getTableCellRendererComponent(JTable table, Object value, public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) { boolean isSelected, boolean hasFocus, int row, int column) {
if (table == null) {
return this;
}
Color fg = null; Color fg = null;
Color bg = null; Color bg = null;
......
...@@ -1334,13 +1334,13 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou ...@@ -1334,13 +1334,13 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou
&& component.getClientProperty("JPasswordField.cutCopyAllowed") != && component.getClientProperty("JPasswordField.cutCopyAllowed") !=
Boolean.TRUE) { Boolean.TRUE) {
//fix for 4793761 //fix for 4793761
StringBuffer txt = null; StringBuilder txt = null;
char echoChar = ((JPasswordField)component).getEchoChar(); char echoChar = ((JPasswordField)component).getEchoChar();
int p0 = Math.min(getDot(), getMark()); int p0 = Math.min(getDot(), getMark());
int p1 = Math.max(getDot(), getMark()); int p1 = Math.max(getDot(), getMark());
for (int i = p0; i < p1; i++) { for (int i = p0; i < p1; i++) {
if (txt == null) { if (txt == null) {
txt = new StringBuffer(); txt = new StringBuilder();
} }
txt.append(echoChar); txt.append(echoChar);
} }
...@@ -1675,7 +1675,6 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou ...@@ -1675,7 +1675,6 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou
} }
return; return;
} }
int adjust = 0;
int offset = e.getOffset(); int offset = e.getOffset();
int length = e.getLength(); int length = e.getLength();
int newDot = dot; int newDot = dot;
...@@ -1759,7 +1758,6 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou ...@@ -1759,7 +1758,6 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou
} }
int offs0 = e.getOffset(); int offs0 = e.getOffset();
int offs1 = offs0 + e.getLength(); int offs1 = offs0 + e.getLength();
int adjust = 0;
int newDot = dot; int newDot = dot;
boolean adjustDotBias = false; boolean adjustDotBias = false;
int newMark = mark; int newMark = mark;
......
...@@ -132,7 +132,7 @@ public class DefaultStyledDocument extends AbstractDocument implements StyledDoc ...@@ -132,7 +132,7 @@ public class DefaultStyledDocument extends AbstractDocument implements StyledDoc
// install the content // install the content
Content c = getContent(); Content c = getContent();
int n = data.length; int n = data.length;
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
ElementSpec es = data[i]; ElementSpec es = data[i];
if (es.getLength() > 0) { if (es.getLength() > 0) {
...@@ -191,7 +191,7 @@ public class DefaultStyledDocument extends AbstractDocument implements StyledDoc ...@@ -191,7 +191,7 @@ public class DefaultStyledDocument extends AbstractDocument implements StyledDoc
// install the content // install the content
Content c = getContent(); Content c = getContent();
int n = data.length; int n = data.length;
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
ElementSpec es = data[i]; ElementSpec es = data[i];
if (es.getLength() > 0) { if (es.getLength() > 0) {
......
...@@ -30,7 +30,6 @@ import java.text.*; ...@@ -30,7 +30,6 @@ import java.text.*;
import java.text.AttributedCharacterIterator.Attribute; import java.text.AttributedCharacterIterator.Attribute;
import java.util.*; import java.util.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.text.*;
/** /**
* <code>InternationalFormatter</code> extends <code>DefaultFormatter</code>, * <code>InternationalFormatter</code> extends <code>DefaultFormatter</code>,
...@@ -875,7 +874,6 @@ public class InternationalFormatter extends DefaultFormatter { ...@@ -875,7 +874,6 @@ public class InternationalFormatter extends DefaultFormatter {
(f instanceof AttributedCharacterIterator.Attribute)) { (f instanceof AttributedCharacterIterator.Attribute)) {
AttributedCharacterIterator.Attribute field = AttributedCharacterIterator.Attribute field =
(AttributedCharacterIterator.Attribute)f; (AttributedCharacterIterator.Attribute)f;
int index = 0;
iterator.first(); iterator.first();
while (iterator.getIndex() < start) { while (iterator.getIndex() < start) {
......
...@@ -35,10 +35,7 @@ import java.util.HashMap; ...@@ -35,10 +35,7 @@ import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Vector; import java.util.Vector;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.*; import java.util.concurrent.*;
...@@ -4058,7 +4055,7 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A ...@@ -4058,7 +4055,7 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A
private static final Object KEYMAP_TABLE = private static final Object KEYMAP_TABLE =
new StringBuilder("JTextComponent_KeymapTable"); new StringBuilder("JTextComponent_KeymapTable");
private JTextComponent editor;
// //
// member variables used for on-the-spot input method // member variables used for on-the-spot input method
// editing style support // editing style support
...@@ -4748,14 +4745,14 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A ...@@ -4748,14 +4745,14 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A
processKeyEvent(ke); processKeyEvent(ke);
} }
} else { } else {
StringBuffer strBuf = new StringBuffer(); StringBuilder strBuf = new StringBuilder();
for (char c = text.current(); commitCount > 0; for (char c = text.current(); commitCount > 0;
c = text.next(), commitCount--) { c = text.next(), commitCount--) {
strBuf.append(c); strBuf.append(c);
} }
// map it to an ActionEvent // map it to an ActionEvent
mapCommittedTextToAction(new String(strBuf)); mapCommittedTextToAction(strBuf.toString());
} }
// Remember latest committed text end index // Remember latest committed text end index
...@@ -4801,7 +4798,7 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A ...@@ -4801,7 +4798,7 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A
private void createComposedTextAttribute(int composedIndex, private void createComposedTextAttribute(int composedIndex,
AttributedCharacterIterator text) { AttributedCharacterIterator text) {
Document doc = getDocument(); Document doc = getDocument();
StringBuffer strBuf = new StringBuffer(); StringBuilder strBuf = new StringBuilder();
// create attributed string with no attributes // create attributed string with no attributes
for (char c = text.setIndex(composedIndex); for (char c = text.setIndex(composedIndex);
...@@ -4809,7 +4806,7 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A ...@@ -4809,7 +4806,7 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A
strBuf.append(c); strBuf.append(c);
} }
composedTextContent = new String(strBuf); composedTextContent = strBuf.toString();
composedTextAttribute = new SimpleAttributeSet(); composedTextAttribute = new SimpleAttributeSet();
composedTextAttribute.addAttribute(StyleConstants.ComposedTextAttribute, composedTextAttribute.addAttribute(StyleConstants.ComposedTextAttribute,
new AttributedString(text, composedIndex, text.getEndIndex())); new AttributedString(text, composedIndex, text.getEndIndex()));
......
...@@ -29,7 +29,6 @@ import java.io.*; ...@@ -29,7 +29,6 @@ import java.io.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.text.*;
/** /**
* <code>MaskFormatter</code> is used to format and edit strings. The behavior * <code>MaskFormatter</code> is used to format and edit strings. The behavior
...@@ -385,7 +384,7 @@ public class MaskFormatter extends DefaultFormatter { ...@@ -385,7 +384,7 @@ public class MaskFormatter extends DefaultFormatter {
*/ */
public String valueToString(Object value) throws ParseException { public String valueToString(Object value) throws ParseException {
String sValue = (value == null) ? "" : value.toString(); String sValue = (value == null) ? "" : value.toString();
StringBuffer result = new StringBuffer(); StringBuilder result = new StringBuilder();
String placeholder = getPlaceholder(); String placeholder = getPlaceholder();
int[] valueCounter = { 0 }; int[] valueCounter = { 0 };
...@@ -484,7 +483,7 @@ public class MaskFormatter extends DefaultFormatter { ...@@ -484,7 +483,7 @@ public class MaskFormatter extends DefaultFormatter {
* Invokes <code>append</code> on the mask characters in * Invokes <code>append</code> on the mask characters in
* <code>mask</code>. * <code>mask</code>.
*/ */
private void append(StringBuffer result, String value, int[] index, private void append(StringBuilder result, String value, int[] index,
String placeholder, MaskCharacter[] mask) String placeholder, MaskCharacter[] mask)
throws ParseException { throws ParseException {
for (int counter = 0, maxCounter = mask.length; for (int counter = 0, maxCounter = mask.length;
...@@ -611,13 +610,13 @@ public class MaskFormatter extends DefaultFormatter { ...@@ -611,13 +610,13 @@ public class MaskFormatter extends DefaultFormatter {
* Removes the literal characters from the passed in string. * Removes the literal characters from the passed in string.
*/ */
private String stripLiteralChars(String string) { private String stripLiteralChars(String string) {
StringBuffer sb = null; StringBuilder sb = null;
int last = 0; int last = 0;
for (int counter = 0, max = string.length(); counter < max; counter++){ for (int counter = 0, max = string.length(); counter < max; counter++){
if (isLiteral(counter)) { if (isLiteral(counter)) {
if (sb == null) { if (sb == null) {
sb = new StringBuffer(); sb = new StringBuilder();
if (counter > 0) { if (counter > 0) {
sb.append(string.substring(0, counter)); sb.append(string.substring(0, counter));
} }
...@@ -715,10 +714,10 @@ public class MaskFormatter extends DefaultFormatter { ...@@ -715,10 +714,10 @@ public class MaskFormatter extends DefaultFormatter {
*/ */
boolean canReplace(ReplaceHolder rh) { boolean canReplace(ReplaceHolder rh) {
// This method is rather long, but much of the burden is in // This method is rather long, but much of the burden is in
// maintaining a String and swapping to a StringBuffer only if // maintaining a String and swapping to a StringBuilder only if
// absolutely necessary. // absolutely necessary.
if (!getAllowsInvalid()) { if (!getAllowsInvalid()) {
StringBuffer replace = null; StringBuilder replace = null;
String text = rh.text; String text = rh.text;
int tl = (text != null) ? text.length() : 0; int tl = (text != null) ? text.length() : 0;
...@@ -737,7 +736,7 @@ public class MaskFormatter extends DefaultFormatter { ...@@ -737,7 +736,7 @@ public class MaskFormatter extends DefaultFormatter {
char aChar = text.charAt(textIndex); char aChar = text.charAt(textIndex);
if (aChar != getCharacter(rh.offset + counter, aChar)) { if (aChar != getCharacter(rh.offset + counter, aChar)) {
if (replace == null) { if (replace == null) {
replace = new StringBuffer(); replace = new StringBuilder();
if (textIndex > 0) { if (textIndex > 0) {
replace.append(text.substring(0, textIndex)); replace.append(text.substring(0, textIndex));
} }
...@@ -758,7 +757,7 @@ public class MaskFormatter extends DefaultFormatter { ...@@ -758,7 +757,7 @@ public class MaskFormatter extends DefaultFormatter {
} }
} }
else if (textIndex > 0) { else if (textIndex > 0) {
replace = new StringBuffer(max); replace = new StringBuilder(max);
replace.append(text.substring(0, textIndex)); replace.append(text.substring(0, textIndex));
replace.append(getLiteral(rh.offset + counter)); replace.append(getLiteral(rh.offset + counter));
if (textIndex < tl) { if (textIndex < tl) {
...@@ -780,7 +779,7 @@ public class MaskFormatter extends DefaultFormatter { ...@@ -780,7 +779,7 @@ public class MaskFormatter extends DefaultFormatter {
else if (textIndex >= tl) { else if (textIndex >= tl) {
// placeholder // placeholder
if (replace == null) { if (replace == null) {
replace = new StringBuffer(); replace = new StringBuilder();
if (text != null) { if (text != null) {
replace.append(text); replace.append(text);
} }
...@@ -863,7 +862,7 @@ public class MaskFormatter extends DefaultFormatter { ...@@ -863,7 +862,7 @@ public class MaskFormatter extends DefaultFormatter {
* Appends the necessary character in <code>formatting</code> at * Appends the necessary character in <code>formatting</code> at
* <code>index</code> to <code>buff</code>. * <code>index</code> to <code>buff</code>.
*/ */
public void append(StringBuffer buff, String formatting, int[] index, public void append(StringBuilder buff, String formatting, int[] index,
String placeholder) String placeholder)
throws ParseException { throws ParseException {
boolean inString = index[0] < formatting.length(); boolean inString = index[0] < formatting.length();
......
...@@ -27,7 +27,6 @@ package javax.swing.text; ...@@ -27,7 +27,6 @@ package javax.swing.text;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
import javax.swing.text.*;
/** /**
* <code>NumberFormatter</code> subclasses <code>InternationalFormatter</code> * <code>NumberFormatter</code> subclasses <code>InternationalFormatter</code>
...@@ -132,7 +131,7 @@ public class NumberFormatter extends InternationalFormatter { ...@@ -132,7 +131,7 @@ public class NumberFormatter extends InternationalFormatter {
DecimalFormatSymbols dfs = getDecimalFormatSymbols(); DecimalFormatSymbols dfs = getDecimalFormatSymbols();
if (dfs != null) { if (dfs != null) {
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
sb.append(dfs.getCurrencySymbol()); sb.append(dfs.getCurrencySymbol());
sb.append(dfs.getDecimalSeparator()); sb.append(dfs.getDecimalSeparator());
...@@ -239,13 +238,6 @@ public class NumberFormatter extends InternationalFormatter { ...@@ -239,13 +238,6 @@ public class NumberFormatter extends InternationalFormatter {
return null; return null;
} }
/**
*/
private boolean isValidInsertionCharacter(char aChar) {
return (Character.isDigit(aChar) || specialChars.indexOf(aChar) != -1);
}
/** /**
* Subclassed to return false if <code>text</code> contains in an invalid * Subclassed to return false if <code>text</code> contains in an invalid
* character to insert, that is, it is not a digit * character to insert, that is, it is not a digit
...@@ -402,28 +394,6 @@ public class NumberFormatter extends InternationalFormatter { ...@@ -402,28 +394,6 @@ public class NumberFormatter extends InternationalFormatter {
return false; return false;
} }
/**
* Returns true if the range offset to length identifies the only
* integer field.
*/
private boolean isOnlyIntegerField(int offset, int length) {
if (isValidMask()) {
int start = getAttributeStart(NumberFormat.Field.INTEGER);
if (start != -1) {
AttributedCharacterIterator iterator = getIterator();
iterator.setIndex(start);
if (offset > start || iterator.getRunLimit(
NumberFormat.Field.INTEGER) > (offset + length)) {
return false;
}
return true;
}
}
return false;
}
/** /**
* Invoked to toggle the sign. For this to work the value class * Invoked to toggle the sign. For this to work the value class
* must have a single arg constructor that takes a String. * must have a single arg constructor that takes a String.
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package javax.swing.text; package javax.swing.text;
import java.util.Vector; import java.util.Vector;
import javax.swing.event.*;
/** /**
* A plain document that maintains no character attributes. The * A plain document that maintains no character attributes. The
...@@ -118,7 +117,7 @@ public class PlainDocument extends AbstractDocument { ...@@ -118,7 +117,7 @@ public class PlainDocument extends AbstractDocument {
Object filterNewlines = getProperty("filterNewlines"); Object filterNewlines = getProperty("filterNewlines");
if ((filterNewlines instanceof Boolean) && filterNewlines.equals(Boolean.TRUE)) { if ((filterNewlines instanceof Boolean) && filterNewlines.equals(Boolean.TRUE)) {
if ((str != null) && (str.indexOf('\n') >= 0)) { if ((str != null) && (str.indexOf('\n') >= 0)) {
StringBuffer filtered = new StringBuffer(str); StringBuilder filtered = new StringBuilder(str);
int n = filtered.length(); int n = filtered.length();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (filtered.charAt(i) == '\n') { if (filtered.charAt(i) == '\n') {
...@@ -204,11 +203,9 @@ public class PlainDocument extends AbstractDocument { ...@@ -204,11 +203,9 @@ public class PlainDocument extends AbstractDocument {
} }
} }
if (hasBreaks) { if (hasBreaks) {
int rmCount = 1;
removed.addElement(rmCandidate); removed.addElement(rmCandidate);
if ((offset + length == rmOffs1) && (lastOffset != rmOffs1) && if ((offset + length == rmOffs1) && (lastOffset != rmOffs1) &&
((index+1) < lineMap.getElementCount())) { ((index+1) < lineMap.getElementCount())) {
rmCount += 1;
Element e = lineMap.getElement(index+1); Element e = lineMap.getElement(index+1);
removed.addElement(e); removed.addElement(e);
rmOffs1 = e.getEndOffset(); rmOffs1 = e.getEndOffset();
......
...@@ -199,7 +199,7 @@ public class TabSet implements Serializable ...@@ -199,7 +199,7 @@ public class TabSet implements Serializable
*/ */
public String toString() { public String toString() {
int tabCount = getTabCount(); int tabCount = getTabCount();
StringBuffer buffer = new StringBuffer("[ "); StringBuilder buffer = new StringBuilder("[ ");
for(int counter = 0; counter < tabCount; counter++) { for(int counter = 0; counter < tabCount; counter++) {
if(counter > 0) if(counter > 0)
......
...@@ -362,7 +362,7 @@ public class FormView extends ComponentView implements ActionListener { ...@@ -362,7 +362,7 @@ public class FormView extends ComponentView implements ActionListener {
*/ */
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
Element element = getElement(); Element element = getElement();
StringBuffer dataBuffer = new StringBuffer(); StringBuilder dataBuffer = new StringBuilder();
HTMLDocument doc = (HTMLDocument)getDocument(); HTMLDocument doc = (HTMLDocument)getDocument();
AttributeSet attr = element.getAttributes(); AttributeSet attr = element.getAttributes();
...@@ -508,7 +508,7 @@ public class FormView extends ComponentView implements ActionListener { ...@@ -508,7 +508,7 @@ public class FormView extends ComponentView implements ActionListener {
*/ */
protected void imageSubmit(String imageData) { protected void imageSubmit(String imageData) {
StringBuffer dataBuffer = new StringBuffer(); StringBuilder dataBuffer = new StringBuilder();
Element elem = getElement(); Element elem = getElement();
HTMLDocument hdoc = (HTMLDocument)elem.getDocument(); HTMLDocument hdoc = (HTMLDocument)elem.getDocument();
getFormData(dataBuffer); getFormData(dataBuffer);
...@@ -589,7 +589,7 @@ public class FormView extends ComponentView implements ActionListener { ...@@ -589,7 +589,7 @@ public class FormView extends ComponentView implements ActionListener {
* @param targetElement the element that triggered the * @param targetElement the element that triggered the
* form submission * form submission
*/ */
void getFormData(StringBuffer buffer) { private void getFormData(StringBuilder buffer) {
Element formE = getFormElement(); Element formE = getFormElement();
if (formE != null) { if (formE != null) {
ElementIterator it = new ElementIterator(formE); ElementIterator it = new ElementIterator(formE);
...@@ -623,7 +623,7 @@ public class FormView extends ComponentView implements ActionListener { ...@@ -623,7 +623,7 @@ public class FormView extends ComponentView implements ActionListener {
* data is loaded in name/value pairs. * data is loaded in name/value pairs.
* *
*/ */
private void loadElementDataIntoBuffer(Element elem, StringBuffer buffer) { private void loadElementDataIntoBuffer(Element elem, StringBuilder buffer) {
AttributeSet attr = elem.getAttributes(); AttributeSet attr = elem.getAttributes();
String name = (String)attr.getAttribute(HTML.Attribute.NAME); String name = (String)attr.getAttribute(HTML.Attribute.NAME);
...@@ -692,29 +692,6 @@ public class FormView extends ComponentView implements ActionListener { ...@@ -692,29 +692,6 @@ public class FormView extends ComponentView implements ActionListener {
} }
if (path != null && path.length() > 0) { if (path != null && path.length() > 0) {
value = path; value = path;
/*
try {
Reader reader = new BufferedReader(new FileReader(path));
StringBuffer buffer = new StringBuffer();
char[] cBuff = new char[1024];
int read;
try {
while ((read = reader.read(cBuff)) != -1) {
buffer.append(cBuff, 0, read);
}
} catch (IOException ioe) {
buffer = null;
}
try {
reader.close();
} catch (IOException ioe) {}
if (buffer != null) {
value = buffer.toString();
}
} catch (IOException ioe) {}
*/
} }
} }
return value; return value;
...@@ -740,7 +717,7 @@ public class FormView extends ComponentView implements ActionListener { ...@@ -740,7 +717,7 @@ public class FormView extends ComponentView implements ActionListener {
* form element. Basically, only items that are selected * form element. Basically, only items that are selected
* and have their name attribute set are added to the buffer. * and have their name attribute set are added to the buffer.
*/ */
private void loadSelectData(AttributeSet attr, StringBuffer buffer) { private void loadSelectData(AttributeSet attr, StringBuilder buffer) {
String name = (String)attr.getAttribute(HTML.Attribute.NAME); String name = (String)attr.getAttribute(HTML.Attribute.NAME);
if (name == null) { if (name == null) {
...@@ -771,7 +748,7 @@ public class FormView extends ComponentView implements ActionListener { ...@@ -771,7 +748,7 @@ public class FormView extends ComponentView implements ActionListener {
* URLEncoder.encode() method before being added to the * URLEncoder.encode() method before being added to the
* buffer. * buffer.
*/ */
private void appendBuffer(StringBuffer buffer, String name, String value) { private void appendBuffer(StringBuilder buffer, String name, String value) {
if (buffer.length() > 0) { if (buffer.length() > 0) {
buffer.append('&'); buffer.append('&');
} }
......
...@@ -691,11 +691,11 @@ public class MinimalHTMLWriter extends AbstractWriter { ...@@ -691,11 +691,11 @@ public class MinimalHTMLWriter extends AbstractWriter {
if (styleNameMapping == null) { if (styleNameMapping == null) {
return style; return style;
} }
StringBuffer sb = null; StringBuilder sb = null;
for (int counter = style.length() - 1; counter >= 0; counter--) { for (int counter = style.length() - 1; counter >= 0; counter--) {
if (!isValidCharacter(style.charAt(counter))) { if (!isValidCharacter(style.charAt(counter))) {
if (sb == null) { if (sb == null) {
sb = new StringBuffer(style); sb = new StringBuilder(style);
} }
sb.setCharAt(counter, 'a'); sb.setCharAt(counter, 'a');
} }
......
...@@ -998,7 +998,7 @@ public class StyleSheet extends StyleContext { ...@@ -998,7 +998,7 @@ public class StyleSheet extends StyleContext {
void addRule(String[] selector, AttributeSet declaration, void addRule(String[] selector, AttributeSet declaration,
boolean isLinked) { boolean isLinked) {
int n = selector.length; int n = selector.length;
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
sb.append(selector[0]); sb.append(selector[0]);
for (int counter = 1; counter < n; counter++) { for (int counter = 1; counter < n; counter++) {
sb.append(' '); sb.append(' ');
......
...@@ -1470,7 +1470,7 @@ class Parser implements DTDConstants { ...@@ -1470,7 +1470,7 @@ class Parser implements DTDConstants {
*/ */
public String parseDTDMarkup() throws IOException { public String parseDTDMarkup() throws IOException {
StringBuffer strBuff = new StringBuffer(); StringBuilder strBuff = new StringBuilder();
ch = readCh(); ch = readCh();
while(true) { while(true) {
switch (ch) { switch (ch) {
......
...@@ -160,7 +160,7 @@ abstract class AbstractFilter extends OutputStream ...@@ -160,7 +160,7 @@ abstract class AbstractFilter extends OutputStream
public void write(byte[] buf, int off, int len) public void write(byte[] buf, int off, int len)
throws IOException throws IOException
{ {
StringBuffer accumulator = null; StringBuilder accumulator = null;
while (len > 0) { while (len > 0) {
short b = (short)buf[off]; short b = (short)buf[off];
...@@ -178,7 +178,7 @@ abstract class AbstractFilter extends OutputStream ...@@ -178,7 +178,7 @@ abstract class AbstractFilter extends OutputStream
char ch = translationTable[b]; char ch = translationTable[b];
if (ch != (char)0) { if (ch != (char)0) {
if (accumulator == null) if (accumulator == null)
accumulator = new StringBuffer(); accumulator = new StringBuilder();
accumulator.append(ch); accumulator.append(ch);
} }
} }
......
...@@ -48,8 +48,6 @@ public class BoundMethodHandle extends MethodHandle { ...@@ -48,8 +48,6 @@ public class BoundMethodHandle extends MethodHandle {
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN); private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
// Constructors in this class *must* be package scoped or private. // Constructors in this class *must* be package scoped or private.
// Exception: JavaMethodHandle constructors are protected.
// (The link between JMH and BMH is temporary.)
/** Bind a direct MH to its receiver (or first ref. argument). /** Bind a direct MH to its receiver (or first ref. argument).
* The JVM will pre-dispatch the MH if it is not already static. * The JVM will pre-dispatch the MH if it is not already static.
...@@ -122,55 +120,6 @@ public class BoundMethodHandle extends MethodHandle { ...@@ -122,55 +120,6 @@ public class BoundMethodHandle extends MethodHandle {
assert(this instanceof JavaMethodHandle); assert(this instanceof JavaMethodHandle);
} }
/** Initialize the current object as a Java method handle.
*/
protected BoundMethodHandle(String entryPointName, MethodType type, boolean matchArity) {
super(Access.TOKEN, null);
MethodHandle entryPoint
= findJavaMethodHandleEntryPoint(this.getClass(),
entryPointName, type, matchArity);
MethodHandleImpl.initType(this, entryPoint.type().dropParameterTypes(0, 1));
this.argument = this; // kludge; get rid of
this.vmargslot = this.type().parameterSlotDepth(0);
initTarget(entryPoint, 0);
assert(this instanceof JavaMethodHandle);
}
private static
MethodHandle findJavaMethodHandleEntryPoint(Class<?> caller,
String name,
MethodType type,
boolean matchArity) {
if (matchArity) type.getClass(); // elicit NPE
List<MemberName> methods = IMPL_NAMES.getMethods(caller, true, name, null, caller);
MethodType foundType = null;
MemberName foundMethod = null;
for (MemberName method : methods) {
if (method.getDeclaringClass() == MethodHandle.class)
continue; // ignore methods inherited from MH class itself
MethodType mtype = method.getMethodType();
if (type != null && type.parameterCount() != mtype.parameterCount())
continue;
else if (foundType == null)
foundType = mtype;
else if (foundType != mtype)
throw newIllegalArgumentException("more than one method named "+name+" in "+caller.getName());
// discard overrides
if (foundMethod == null)
foundMethod = method;
else if (foundMethod.getDeclaringClass().isAssignableFrom(method.getDeclaringClass()))
foundMethod = method;
}
if (foundMethod == null)
throw newIllegalArgumentException("no method named "+name+" in "+caller.getName());
MethodHandle entryPoint = MethodHandleImpl.findMethod(IMPL_TOKEN, foundMethod, true, caller);
if (type != null) {
MethodType epType = type.insertParameterTypes(0, entryPoint.type().parameterType(0));
entryPoint = MethodHandles.convertArguments(entryPoint, epType);
}
return entryPoint;
}
/** Make sure the given {@code argument} can be used as {@code argnum}-th /** Make sure the given {@code argument} can be used as {@code argnum}-th
* parameter of the given method handle {@code mh}, which must be a reference. * parameter of the given method handle {@code mh}, which must be a reference.
* <p> * <p>
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.*; import java.dyn.*;
import static sun.dyn.MemberName.uncaughtException;
/** /**
* Parts of CallSite known to the JVM. * Parts of CallSite known to the JVM.
...@@ -49,18 +50,21 @@ public class CallSiteImpl { ...@@ -49,18 +50,21 @@ public class CallSiteImpl {
} }
CallSite site; CallSite site;
try { try {
if (bootstrapMethod.type().parameterCount() == 3) Object binding;
site = bootstrapMethod.<CallSite>invokeExact(caller, name, type); if (false) // switch when invokeGeneric works
else if (bootstrapMethod.type().parameterCount() == 4) binding = bootstrapMethod.invokeGeneric(caller, name, type);
site = bootstrapMethod.<CallSite>invokeExact(caller, name, type,
!(info instanceof java.lang.annotation.Annotation[]) ? null
: (java.lang.annotation.Annotation[]) info);
else else
throw new InternalError("bad BSM: "+bootstrapMethod); binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type });
if (!(site instanceof CallSite)) //System.out.println("BSM for "+name+type+" => "+binding);
throw new InvokeDynamicBootstrapError("class bootstrap method failed to create a call site: "+caller); if (binding instanceof CallSite) {
PRIVATE_INITIALIZE_CALL_SITE.<void>invokeExact(site, site = (CallSite) binding;
name, type, } else if (binding instanceof MethodHandleProvider) {
MethodHandle target = ((MethodHandleProvider) binding).asMethodHandle();
site = new ConstantCallSite(target);
} else {
throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite");
}
PRIVATE_INITIALIZE_CALL_SITE.<void>invokeExact(site, name, type,
callerMethod, callerBCI); callerMethod, callerBCI);
assert(site.getTarget() != null); assert(site.getTarget() != null);
assert(site.getTarget().type().equals(type)); assert(site.getTarget().type().equals(type));
...@@ -77,11 +81,18 @@ public class CallSiteImpl { ...@@ -77,11 +81,18 @@ public class CallSiteImpl {
// This method is private in CallSite because it touches private fields in CallSite. // This method is private in CallSite because it touches private fields in CallSite.
// These private fields (vmmethod, vmindex) are specific to the JVM. // These private fields (vmmethod, vmindex) are specific to the JVM.
private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE = private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE;
static {
try {
PRIVATE_INITIALIZE_CALL_SITE =
MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM", MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM",
MethodType.methodType(void.class, MethodType.methodType(void.class,
String.class, MethodType.class, String.class, MethodType.class,
MemberName.class, int.class)); MemberName.class, int.class));
} catch (NoAccessException ex) {
throw uncaughtException(ex);
}
}
public static void setCallSiteTarget(Access token, CallSite site, MethodHandle target) { public static void setCallSiteTarget(Access token, CallSite site, MethodHandle target) {
Access.check(token); Access.check(token);
......
...@@ -25,12 +25,8 @@ ...@@ -25,12 +25,8 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle; import java.lang.reflect.*;
import java.dyn.MethodType;
import java.dyn.NoAccessException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import static sun.dyn.MemberName.newIllegalArgumentException; import static sun.dyn.MemberName.newIllegalArgumentException;
/** /**
...@@ -119,7 +115,7 @@ class FilterGeneric { ...@@ -119,7 +115,7 @@ class FilterGeneric {
static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) { static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
FilterGeneric fgen = of(kind, pos, filter.type(), target.type()); FilterGeneric fgen = of(kind, pos, filter.type(), target.type());
return fgen.makeInstance(kind, pos, filter, target); return fgen.makeInstance(kind, pos, filter, target).asMethodHandle();
} }
/** Return the adapter information for this target and filter type. */ /** Return the adapter information for this target and filter type. */
......
...@@ -25,9 +25,8 @@ ...@@ -25,9 +25,8 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle; import static sun.dyn.MemberName.uncaughtException;
import java.dyn.MethodType;
/** /**
* Unary function composition, useful for many small plumbing jobs. * Unary function composition, useful for many small plumbing jobs.
...@@ -51,8 +50,16 @@ public class FilterOneArgument extends JavaMethodHandle { ...@@ -51,8 +50,16 @@ public class FilterOneArgument extends JavaMethodHandle {
return target.invokeExact(filteredArgument); return target.invokeExact(filteredArgument);
} }
private static final MethodHandle INVOKE = private static final MethodHandle INVOKE;
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke", MethodType.genericMethodType(1)); static {
try {
INVOKE =
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke",
MethodType.genericMethodType(1));
} catch (NoAccessException ex) {
throw uncaughtException(ex);
}
}
protected FilterOneArgument(MethodHandle filter, MethodHandle target) { protected FilterOneArgument(MethodHandle filter, MethodHandle target) {
super(INVOKE); super(INVOKE);
......
...@@ -25,15 +25,9 @@ ...@@ -25,15 +25,9 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle; import java.lang.reflect.*;
import java.dyn.MethodHandles; import sun.dyn.util.*;
import java.dyn.MethodType;
import java.dyn.NoAccessException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import sun.dyn.util.ValueConversions;
import sun.dyn.util.Wrapper;
/** /**
* Adapters which mediate between incoming calls which are generic * Adapters which mediate between incoming calls which are generic
......
...@@ -25,10 +25,7 @@ ...@@ -25,10 +25,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.MethodHandle; import java.dyn.*;
import java.dyn.MethodHandles;
import java.dyn.MethodType;
/** /**
* Construction and caching of often-used invokers. * Construction and caching of often-used invokers.
...@@ -63,8 +60,11 @@ public class Invokers { ...@@ -63,8 +60,11 @@ public class Invokers {
public MethodHandle exactInvoker() { public MethodHandle exactInvoker() {
MethodHandle invoker = exactInvoker; MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker; if (invoker != null) return invoker;
invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType); try {
if (invoker == null) throw new InternalError("JVM cannot find invoker for "+targetType); invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
} catch (NoAccessException ex) {
throw new InternalError("JVM cannot find invoker for "+targetType);
}
assert(invokerType(targetType) == invoker.type()); assert(invokerType(targetType) == invoker.type());
exactInvoker = invoker; exactInvoker = invoker;
return invoker; return invoker;
......
...@@ -23,8 +23,9 @@ ...@@ -23,8 +23,9 @@
* questions. * questions.
*/ */
package java.dyn; package sun.dyn;
import java.dyn.*;
import sun.dyn.Access; import sun.dyn.Access;
/** /**
...@@ -168,70 +169,4 @@ public abstract class JavaMethodHandle ...@@ -168,70 +169,4 @@ public abstract class JavaMethodHandle
protected JavaMethodHandle(MethodHandle entryPoint) { protected JavaMethodHandle(MethodHandle entryPoint) {
super(entryPoint); super(entryPoint);
} }
/**
* Create a method handle whose entry point is a non-static method
* visible in the exact (most specific) class of
* the newly constructed object.
* <p>
* The method is specified by name and type, as if via this expression:
* {@code MethodHandles.lookup().findVirtual(this.getClass(), name, type)}.
* The class defining the method might be an anonymous inner class.
* <p>
* The method handle type of {@code this} (i.e, the fully constructed object)
* will be the given method handle type.
* A call to {@code this} will invoke the selected method.
* The receiver argument will be bound to {@code this} on every method
* handle invocation.
* <p>
* <i>Rationale:</i>
* Although this constructor may seem to be a mere luxury,
* it is not subsumed by the more general constructor which
* takes any {@code MethodHandle} as the entry point argument.
* In order to convert an entry point name to a method handle,
* the self-class of the object is required (in order to do
* the lookup). The self-class, in turn, is generally not
* available at the time of the constructor invocation,
* due to the rules of Java and the JVM verifier.
* One cannot call {@code this.getClass()}, because
* the value of {@code this} is inaccessible at the point
* of the constructor call. (Changing this would require
* change to the Java language, verifiers, and compilers.)
* In particular, this constructor allows {@code JavaMethodHandle}s
* to be created in combination with the anonymous inner class syntax.
* @param entryPointName the name of the entry point method
* @param type (optional) the desired type of the method handle
*/
protected JavaMethodHandle(String entryPointName, MethodType type) {
super(entryPointName, type, true);
}
/**
* Create a method handle whose entry point is a non-static method
* visible in the exact (most specific) class of
* the newly constructed object.
* <p>
* The method is specified only by name.
* There must be exactly one method of that name visible in the object class,
* either inherited or locally declared.
* (That is, the method must not be overloaded.)
* <p>
* The method handle type of {@code this} (i.e, the fully constructed object)
* will be the same as the type of the selected non-static method.
* The receiver argument will be bound to {@code this} on every method
* handle invocation.
* <p>ISSUE: This signature wildcarding feature does not correspond to
* any MethodHandles.Lookup API element. Can we eliminate it?
* Alternatively, it is useful for naming non-overloaded methods.
* Shall we make type arguments optional in the Lookup methods,
* throwing an error in cases of ambiguity?
* <p>
* For this method's rationale, see the documentation
* for {@link #JavaMethodHandle(String,MethodType)}.
* @param entryPointName the name of the entry point method
*/
protected JavaMethodHandle(String entryPointName) {
super(entryPointName, (MethodType) null, false);
}
} }
...@@ -521,6 +521,11 @@ public final class MemberName implements Member, Cloneable { ...@@ -521,6 +521,11 @@ public final class MemberName implements Member, Cloneable {
if (lookupClass != null) message += ", from " + lookupClass.getName(); if (lookupClass != null) message += ", from " + lookupClass.getName();
return new NoAccessException(message); return new NoAccessException(message);
} }
public static Error uncaughtException(Exception ex) {
Error err = new InternalError("uncaught exception");
err.initCause(ex);
return err;
}
/** Actually making a query requires an access check. */ /** Actually making a query requires an access check. */
public static Factory getFactory(Access token) { public static Factory getFactory(Access token) {
...@@ -641,7 +646,7 @@ public final class MemberName implements Member, Cloneable { ...@@ -641,7 +646,7 @@ public final class MemberName implements Member, Cloneable {
* If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown. * If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown.
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in. * Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
*/ */
public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) { public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) throws NoAccessException {
MemberName result = resolveOrNull(m, searchSupers, lookupClass); MemberName result = resolveOrNull(m, searchSupers, lookupClass);
if (result != null) if (result != null)
return result; return result;
......
...@@ -25,11 +25,8 @@ ...@@ -25,11 +25,8 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle;
import java.dyn.MethodHandles;
import java.dyn.MethodHandles.Lookup; import java.dyn.MethodHandles.Lookup;
import java.dyn.MethodType;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import sun.dyn.util.VerifyType; import sun.dyn.util.VerifyType;
...@@ -46,6 +43,7 @@ import sun.dyn.util.Wrapper; ...@@ -46,6 +43,7 @@ import sun.dyn.util.Wrapper;
import sun.misc.Unsafe; import sun.misc.Unsafe;
import static sun.dyn.MemberName.newIllegalArgumentException; import static sun.dyn.MemberName.newIllegalArgumentException;
import static sun.dyn.MemberName.newNoAccessException; import static sun.dyn.MemberName.newNoAccessException;
import static sun.dyn.MemberName.uncaughtException;
/** /**
* Base class for method handles, containing JVM-specific fields and logic. * Base class for method handles, containing JVM-specific fields and logic.
...@@ -173,7 +171,7 @@ public abstract class MethodHandleImpl { ...@@ -173,7 +171,7 @@ public abstract class MethodHandleImpl {
*/ */
public static public static
MethodHandle findMethod(Access token, MemberName method, MethodHandle findMethod(Access token, MemberName method,
boolean doDispatch, Class<?> lookupClass) { boolean doDispatch, Class<?> lookupClass) throws NoAccessException {
Access.check(token); // only trusted calls Access.check(token); // only trusted calls
MethodType mtype = method.getMethodType(); MethodType mtype = method.getMethodType();
if (!method.isStatic()) { if (!method.isStatic()) {
...@@ -320,7 +318,7 @@ public abstract class MethodHandleImpl { ...@@ -320,7 +318,7 @@ public abstract class MethodHandleImpl {
try { try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true)); VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
throw new InternalError(""); throw uncaughtException(ex);
} }
} }
// Corresponding generic constructor types: // Corresponding generic constructor types:
...@@ -416,9 +414,7 @@ public abstract class MethodHandleImpl { ...@@ -416,9 +414,7 @@ public abstract class MethodHandleImpl {
f = c.getDeclaredField(field.getName()); f = c.getDeclaredField(field.getName());
return unsafe.staticFieldBase(f); return unsafe.staticFieldBase(f);
} catch (Exception ee) { } catch (Exception ee) {
Error e = new InternalError(); throw uncaughtException(ee);
e.initCause(ee);
throw e;
} }
} }
...@@ -473,10 +469,8 @@ public abstract class MethodHandleImpl { ...@@ -473,10 +469,8 @@ public abstract class MethodHandleImpl {
MethodHandle mh; MethodHandle mh;
try { try {
mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type); mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
} catch (NoAccessException ee) { } catch (NoAccessException ex) {
Error e = new InternalError("name,type="+name+type); throw uncaughtException(ex);
e.initCause(ee);
throw e;
} }
if (evclass != vclass || (!isStatic && ecclass != cclass)) { if (evclass != vclass || (!isStatic && ecclass != cclass)) {
MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic); MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
...@@ -543,10 +537,8 @@ public abstract class MethodHandleImpl { ...@@ -543,10 +537,8 @@ public abstract class MethodHandleImpl {
MethodHandle mh; MethodHandle mh;
try { try {
mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type); mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
} catch (NoAccessException ee) { } catch (NoAccessException ex) {
Error e = new InternalError("name,type="+name+type); throw uncaughtException(ex);
e.initCause(ee);
throw e;
} }
if (caclass != null) { if (caclass != null) {
MethodType strongType = FieldAccessor.atype(caclass, isSetter); MethodType strongType = FieldAccessor.atype(caclass, isSetter);
...@@ -1031,7 +1023,7 @@ public abstract class MethodHandleImpl { ...@@ -1031,7 +1023,7 @@ public abstract class MethodHandleImpl {
try { try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true)); VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
throw new InternalError(""); throw uncaughtException(ex);
} }
} }
} }
...@@ -1167,7 +1159,7 @@ public abstract class MethodHandleImpl { ...@@ -1167,7 +1159,7 @@ public abstract class MethodHandleImpl {
try { try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true)); VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
throw new InternalError(""); throw uncaughtException(ex);
} }
} }
} }
...@@ -1207,9 +1199,16 @@ public abstract class MethodHandleImpl { ...@@ -1207,9 +1199,16 @@ public abstract class MethodHandleImpl {
return AdapterMethodHandle.makeRetypeRaw(token, type, THROW_EXCEPTION); return AdapterMethodHandle.makeRetypeRaw(token, type, THROW_EXCEPTION);
} }
static final MethodHandle THROW_EXCEPTION static final MethodHandle THROW_EXCEPTION;
static {
try {
THROW_EXCEPTION
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
MethodType.methodType(Empty.class, Throwable.class)); MethodType.methodType(Empty.class, Throwable.class));
} catch (NoAccessException ex) {
throw new RuntimeException(ex);
}
}
static <T extends Throwable> Empty throwException(T t) throws T { throw t; } static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
public static String getNameString(Access token, MethodHandle target) { public static String getNameString(Access token, MethodHandle target) {
......
...@@ -25,9 +25,7 @@ ...@@ -25,9 +25,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.CallSite; import java.dyn.*;
import java.dyn.MethodHandle;
import java.dyn.MethodType;
import java.dyn.MethodHandles.Lookup; import java.dyn.MethodHandles.Lookup;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field; import java.lang.reflect.Field;
...@@ -324,18 +322,24 @@ class MethodHandleNatives { ...@@ -324,18 +322,24 @@ class MethodHandleNatives {
*/ */
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind, static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type) { Class<?> defc, String name, Object type) {
Lookup lookup = IMPL_LOOKUP.in(callerClass); try {
switch (refKind) { Lookup lookup = IMPL_LOOKUP.in(callerClass);
case REF_getField: return lookup.findGetter( defc, name, (Class<?>) type ); switch (refKind) {
case REF_getStatic: return lookup.findStaticGetter( defc, name, (Class<?>) type ); case REF_getField: return lookup.findGetter( defc, name, (Class<?>) type );
case REF_putField: return lookup.findSetter( defc, name, (Class<?>) type ); case REF_getStatic: return lookup.findStaticGetter( defc, name, (Class<?>) type );
case REF_putStatic: return lookup.findStaticSetter( defc, name, (Class<?>) type ); case REF_putField: return lookup.findSetter( defc, name, (Class<?>) type );
case REF_invokeVirtual: return lookup.findVirtual( defc, name, (MethodType) type ); case REF_putStatic: return lookup.findStaticSetter( defc, name, (Class<?>) type );
case REF_invokeStatic: return lookup.findStatic( defc, name, (MethodType) type ); case REF_invokeVirtual: return lookup.findVirtual( defc, name, (MethodType) type );
case REF_invokeSpecial: return lookup.findSpecial( defc, name, (MethodType) type, callerClass ); case REF_invokeStatic: return lookup.findStatic( defc, name, (MethodType) type );
case REF_newInvokeSpecial: return lookup.findConstructor( defc, (MethodType) type ); case REF_invokeSpecial: return lookup.findSpecial( defc, name, (MethodType) type, callerClass );
case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type ); case REF_newInvokeSpecial: return lookup.findConstructor( defc, (MethodType) type );
case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type );
}
throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
} catch (NoAccessException ex) {
Error err = new IncompatibleClassChangeError();
err.initCause(ex);
throw err;
} }
throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
} }
} }
...@@ -25,11 +25,7 @@ ...@@ -25,11 +25,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle;
import java.dyn.MethodHandles;
import java.dyn.MethodType;
import java.dyn.NoAccessException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
......
...@@ -25,11 +25,7 @@ ...@@ -25,11 +25,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.JavaMethodHandle; import java.dyn.*;
import java.dyn.MethodHandle;
import java.dyn.MethodHandles;
import java.dyn.MethodType;
import java.dyn.NoAccessException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import sun.dyn.util.ValueConversions; import sun.dyn.util.ValueConversions;
......
...@@ -34,6 +34,7 @@ import java.util.List; ...@@ -34,6 +34,7 @@ import java.util.List;
import sun.dyn.Access; import sun.dyn.Access;
import sun.dyn.AdapterMethodHandle; import sun.dyn.AdapterMethodHandle;
import sun.dyn.MethodHandleImpl; import sun.dyn.MethodHandleImpl;
import static sun.dyn.MemberName.uncaughtException;
public class ValueConversions { public class ValueConversions {
private static final Access IMPL_TOKEN = Access.getToken(); private static final Access IMPL_TOKEN = Access.getToken();
...@@ -148,11 +149,16 @@ public class ValueConversions { ...@@ -148,11 +149,16 @@ public class ValueConversions {
// look up the method // look up the method
String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : ""); String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
MethodType type = unboxType(wrap, raw); MethodType type = unboxType(wrap, raw);
if (!exact) if (!exact) {
// actually, type is wrong; the Java method takes Object try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase()); // actually, type is wrong; the Java method takes Object
else mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
} catch (NoAccessException ex) {
mh = null;
}
} else {
mh = retype(type, unbox(wrap, !exact, raw)); mh = retype(type, unbox(wrap, !exact, raw));
}
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); cache.put(wrap, mh);
return mh; return mh;
...@@ -280,10 +286,15 @@ public class ValueConversions { ...@@ -280,10 +286,15 @@ public class ValueConversions {
// look up the method // look up the method
String name = "box" + wrap.simpleName() + (raw ? "Raw" : ""); String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
MethodType type = boxType(wrap, raw); MethodType type = boxType(wrap, raw);
if (exact) if (exact) {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); try {
else mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
} catch (NoAccessException ex) {
mh = null;
}
} else {
mh = retype(type.erase(), box(wrap, !exact, raw)); mh = retype(type.erase(), box(wrap, !exact, raw));
}
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); cache.put(wrap, mh);
return mh; return mh;
...@@ -394,10 +405,15 @@ public class ValueConversions { ...@@ -394,10 +405,15 @@ public class ValueConversions {
// look up the method // look up the method
String name = "reboxRaw" + wrap.simpleName(); String name = "reboxRaw" + wrap.simpleName();
MethodType type = reboxType(wrap); MethodType type = reboxType(wrap);
if (exact) if (exact) {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); try {
else mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
} catch (NoAccessException ex) {
mh = null;
}
} else {
mh = retype(IDENTITY.type(), rebox(wrap, !exact)); mh = retype(IDENTITY.type(), rebox(wrap, !exact));
}
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); cache.put(wrap, mh);
return mh; return mh;
...@@ -474,7 +490,11 @@ public class ValueConversions { ...@@ -474,7 +490,11 @@ public class ValueConversions {
mh = EMPTY; mh = EMPTY;
break; break;
case INT: case LONG: case FLOAT: case DOUBLE: case INT: case LONG: case FLOAT: case DOUBLE:
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type); try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
} catch (NoAccessException ex) {
mh = null;
}
break; break;
} }
if (mh != null) { if (mh != null) {
...@@ -549,8 +569,8 @@ public class ValueConversions { ...@@ -549,8 +569,8 @@ public class ValueConversions {
ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType); ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType); IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1)); EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1));
} catch (RuntimeException ex) { } catch (Exception ex) {
throw ex; throw uncaughtException(ex);
} }
} }
......
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.pisces;
import java.util.Iterator;
class Curve {
float ax, ay, bx, by, cx, cy, dx, dy;
float dax, day, dbx, dby;
Curve() {
}
void set(float[] points, int type) {
switch(type) {
case 8:
set(points[0], points[1],
points[2], points[3],
points[4], points[5],
points[6], points[7]);
break;
case 6:
set(points[0], points[1],
points[2], points[3],
points[4], points[5]);
break;
default:
throw new InternalError("Curves can only be cubic or quadratic");
}
}
void set(float x1, float y1,
float x2, float y2,
float x3, float y3,
float x4, float y4)
{
ax = 3 * (x2 - x3) + x4 - x1;
ay = 3 * (y2 - y3) + y4 - y1;
bx = 3 * (x1 - 2 * x2 + x3);
by = 3 * (y1 - 2 * y2 + y3);
cx = 3 * (x2 - x1);
cy = 3 * (y2 - y1);
dx = x1;
dy = y1;
dax = 3 * ax; day = 3 * ay;
dbx = 2 * bx; dby = 2 * by;
}
void set(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
ax = ay = 0f;
bx = x1 - 2 * x2 + x3;
by = y1 - 2 * y2 + y3;
cx = 2 * (x2 - x1);
cy = 2 * (y2 - y1);
dx = x1;
dy = y1;
dax = 0; day = 0;
dbx = 2 * bx; dby = 2 * by;
}
float xat(float t) {
return t * (t * (t * ax + bx) + cx) + dx;
}
float yat(float t) {
return t * (t * (t * ay + by) + cy) + dy;
}
float dxat(float t) {
return t * (t * dax + dbx) + cx;
}
float dyat(float t) {
return t * (t * day + dby) + cy;
}
private float ddxat(float t) {
return 2 * dax * t + dbx;
}
private float ddyat(float t) {
return 2 * day * t + dby;
}
int dxRoots(float[] roots, int off) {
return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
}
int dyRoots(float[] roots, int off) {
return Helpers.quadraticRoots(day, dby, cy, roots, off);
}
int infPoints(float[] pts, int off) {
// inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
// Fortunately, this turns out to be quadratic, so there are at
// most 2 inflection points.
final float a = dax * dby - dbx * day;
final float b = 2 * (cy * dax - day * cx);
final float c = cy * dbx - cx * dby;
return Helpers.quadraticRoots(a, b, c, pts, off);
}
// finds points where the first and second derivative are
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
// * is a dot product). Unfortunately, we have to solve a cubic.
private int perpendiculardfddf(float[] pts, int off, final float err) {
assert pts.length >= off + 4;
// these are the coefficients of g(t):
final float a = 2*(dax*dax + day*day);
final float b = 3*(dax*dbx + day*dby);
final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby;
final float d = dbx*cx + dby*cy;
// TODO: We might want to divide the polynomial by a to make the
// coefficients smaller. This won't change the roots.
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, err, 0f, 1f);
}
// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
// a variant of the false position algorithm to find the roots. False
// position requires that 2 initial values x0,x1 be given, and that the
// function must have opposite signs at those values. To find such
// values, we need the local extrema of the ROC function, for which we
// need the roots of its derivative; however, it's harder to find the
// roots of the derivative in this case than it is to find the roots
// of the original function. So, we find all points where this curve's
// first and second derivative are perpendicular, and we pretend these
// are our local extrema. There are at most 3 of these, so we will check
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
// points, so roc-w can have at least 6 roots. This shouldn't be a
// problem for what we're trying to do (draw a nice looking curve).
int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {
// no OOB exception, because by now off<=6, and roots.length >= 10
assert off <= 6 && roots.length >= 10;
int ret = off;
int numPerpdfddf = perpendiculardfddf(roots, off, err);
float t0 = 0, ft0 = ROCsq(t0) - w*w;
roots[off + numPerpdfddf] = 1f; // always check interval end points
numPerpdfddf++;
for (int i = off; i < off + numPerpdfddf; i++) {
float t1 = roots[i], ft1 = ROCsq(t1) - w*w;
if (ft0 == 0f) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0f) { // have opposite signs
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
// ROC(t) >= 0 for all t.
roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
}
t0 = t1;
ft0 = ft1;
}
return ret - off;
}
private static float eliminateInf(float x) {
return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :
(x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
}
// A slight modification of the false position algorithm on wikipedia.
// This only works for the ROCsq-x functions. It might be nice to have
// the function as an argument, but that would be awkward in java6.
// It is something to consider for java7, depending on how closures
// and function objects turn out. Same goes for the newton's method
// algorithm in Helpers.java
private float falsePositionROCsqMinusX(float x0, float x1,
final float x, final float err)
{
final int iterLimit = 100;
int side = 0;
float t = x1, ft = eliminateInf(ROCsq(t) - x);
float s = x0, fs = eliminateInf(ROCsq(s) - x);
float r = s, fr;
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
r = (fs * t - ft * s) / (fs - ft);
fr = ROCsq(r) - x;
if (fr * ft > 0) {// have the same sign
ft = fr; t = r;
if (side < 0) {
fs /= (1 << (-side));
side--;
} else {
side = -1;
}
} else if (fr * fs > 0) {
fs = fr; s = r;
if (side > 0) {
ft /= (1 << side);
side++;
} else {
side = 1;
}
} else {
break;
}
}
return r;
}
// returns the radius of curvature squared at t of this curve
// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
private float ROCsq(final float t) {
final float dx = dxat(t);
final float dy = dyat(t);
final float ddx = ddxat(t);
final float ddy = ddyat(t);
final float dx2dy2 = dx*dx + dy*dy;
final float ddx2ddy2 = ddx*ddx + ddy*ddy;
final float ddxdxddydy = ddx*dx + ddy*dy;
float ret = ((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy))*dx2dy2;
return ret;
}
// curve to be broken should be in pts[0]
// this will change the contents of both pts and Ts
// TODO: There's no reason for Ts to be an array. All we need is a sequence
// of t values at which to subdivide. An array statisfies this condition,
// but is unnecessarily restrictive. Ts should be an Iterator<Float> instead.
// Doing this will also make dashing easier, since we could easily make
// LengthIterator an Iterator<Float> and feed it to this function to simplify
// the loop in Dasher.somethingTo.
static Iterator<float[]> breakPtsAtTs(final float[][] pts, final int type,
final float[] Ts, final int numTs)
{
assert pts.length >= 2 && pts[0].length >= 8 && numTs <= Ts.length;
return new Iterator<float[]>() {
int nextIdx = 0;
int nextCurveIdx = 0;
float prevT = 0;
@Override public boolean hasNext() {
return nextCurveIdx < numTs + 1;
}
@Override public float[] next() {
float[] ret;
if (nextCurveIdx < numTs) {
float curT = Ts[nextCurveIdx];
float splitT = (curT - prevT) / (1 - prevT);
Helpers.subdivideAt(splitT,
pts[nextIdx], 0,
pts[nextIdx], 0,
pts[1-nextIdx], 0, type);
updateTs(Ts, Ts[nextCurveIdx], nextCurveIdx + 1, numTs - nextCurveIdx - 1);
ret = pts[nextIdx];
nextIdx = 1 - nextIdx;
} else {
ret = pts[nextIdx];
}
nextCurveIdx++;
return ret;
}
@Override public void remove() {}
};
}
// precondition: ts[off]...ts[off+len-1] must all be greater than t.
private static void updateTs(float[] ts, final float t, final int off, final int len) {
for (int i = off; i < off + len; i++) {
ts[i] = (ts[i] - t) / (1 - t);
}
}
}
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
package sun.java2d.pisces; package sun.java2d.pisces;
import sun.awt.geom.PathConsumer2D;
/** /**
* The <code>Dasher</code> class takes a series of linear commands * The <code>Dasher</code> class takes a series of linear commands
* (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
...@@ -36,18 +38,16 @@ package sun.java2d.pisces; ...@@ -36,18 +38,16 @@ package sun.java2d.pisces;
* semantics are unclear. * semantics are unclear.
* *
*/ */
public class Dasher implements LineSink { public class Dasher implements sun.awt.geom.PathConsumer2D {
private final LineSink output;
private final PathConsumer2D out;
private final float[] dash; private final float[] dash;
private final float startPhase; private final float startPhase;
private final boolean startDashOn; private final boolean startDashOn;
private final int startIdx; private final int startIdx;
private final float m00, m10, m01, m11;
private final float det;
private boolean firstDashOn;
private boolean starting; private boolean starting;
private boolean needsMoveTo;
private int idx; private int idx;
private boolean dashOn; private boolean dashOn;
...@@ -55,28 +55,23 @@ public class Dasher implements LineSink { ...@@ -55,28 +55,23 @@ public class Dasher implements LineSink {
private float sx, sy; private float sx, sy;
private float x0, y0; private float x0, y0;
private float sx1, sy1;
// temporary storage for the current curve
private float[] curCurvepts;
/** /**
* Constructs a <code>Dasher</code>. * Constructs a <code>Dasher</code>.
* *
* @param output an output <code>LineSink</code>. * @param out an output <code>PathConsumer2D</code>.
* @param dash an array of <code>int</code>s containing the dash pattern * @param dash an array of <code>float</code>s containing the dash pattern
* @param phase an <code>int</code> containing the dash phase * @param phase a <code>float</code> containing the dash phase
* @param transform a <code>Transform4</code> object indicating
* the transform that has been previously applied to all incoming
* coordinates. This is required in order to compute dash lengths
* properly.
*/ */
public Dasher(LineSink output, public Dasher(PathConsumer2D out, float[] dash, float phase) {
float[] dash, float phase,
float a00, float a01, float a10, float a11) {
if (phase < 0) { if (phase < 0) {
throw new IllegalArgumentException("phase < 0 !"); throw new IllegalArgumentException("phase < 0 !");
} }
this.output = output; this.out = out;
// Normalize so 0 <= phase < dash[0] // Normalize so 0 <= phase < dash[0]
int idx = 0; int idx = 0;
...@@ -92,16 +87,19 @@ public class Dasher implements LineSink { ...@@ -92,16 +87,19 @@ public class Dasher implements LineSink {
this.startPhase = this.phase = phase; this.startPhase = this.phase = phase;
this.startDashOn = dashOn; this.startDashOn = dashOn;
this.startIdx = idx; this.startIdx = idx;
this.starting = true;
m00 = a00; // we need curCurvepts to be able to contain 2 curves because when
m01 = a01; // dashing curves, we need to subdivide it
m10 = a10; curCurvepts = new float[8 * 2];
m11 = a11;
det = m00 * m11 - m01 * m10;
} }
public void moveTo(float x0, float y0) { public void moveTo(float x0, float y0) {
output.moveTo(x0, y0); if (firstSegidx > 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
needsMoveTo = true;
this.idx = startIdx; this.idx = startIdx;
this.dashOn = this.startDashOn; this.dashOn = this.startDashOn;
this.phase = this.startPhase; this.phase = this.startPhase;
...@@ -110,104 +108,398 @@ public class Dasher implements LineSink { ...@@ -110,104 +108,398 @@ public class Dasher implements LineSink {
this.starting = true; this.starting = true;
} }
public void lineJoin() { private void emitSeg(float[] buf, int off, int type) {
output.lineJoin(); switch (type) {
case 8:
out.curveTo(buf[off+0], buf[off+1],
buf[off+2], buf[off+3],
buf[off+4], buf[off+5]);
break;
case 6:
out.quadTo(buf[off+0], buf[off+1],
buf[off+2], buf[off+3]);
break;
case 4:
out.lineTo(buf[off], buf[off+1]);
}
}
private void emitFirstSegments() {
for (int i = 0; i < firstSegidx; ) {
emitSeg(firstSegmentsBuffer, i+1, (int)firstSegmentsBuffer[i]);
i += (((int)firstSegmentsBuffer[i]) - 1);
}
firstSegidx = 0;
} }
private void goTo(float x1, float y1) { // We don't emit the first dash right away. If we did, caps would be
// drawn on it, but we need joins to be drawn if there's a closePath()
// So, we store the path elements that make up the first dash in the
// buffer below.
private float[] firstSegmentsBuffer = new float[7];
private int firstSegidx = 0;
// precondition: pts must be in relative coordinates (relative to x0,y0)
// fullCurve is true iff the curve in pts has not been split.
private void goTo(float[] pts, int off, final int type) {
float x = pts[off + type - 4];
float y = pts[off + type - 3];
if (dashOn) { if (dashOn) {
if (starting) { if (starting) {
this.sx1 = x1; firstSegmentsBuffer = Helpers.widenArray(firstSegmentsBuffer,
this.sy1 = y1; firstSegidx, type - 2);
firstDashOn = true; firstSegmentsBuffer[firstSegidx++] = type;
starting = false; System.arraycopy(pts, off, firstSegmentsBuffer, firstSegidx, type - 2);
firstSegidx += type - 2;
} else {
if (needsMoveTo) {
out.moveTo(x0, y0);
needsMoveTo = false;
}
emitSeg(pts, off, type);
} }
output.lineTo(x1, y1);
} else { } else {
if (starting) { starting = false;
firstDashOn = false; needsMoveTo = true;
starting = false;
}
output.moveTo(x1, y1);
} }
this.x0 = x1; this.x0 = x;
this.y0 = y1; this.y0 = y;
} }
public void lineTo(float x1, float y1) { public void lineTo(float x1, float y1) {
// The widened line is squished to a 0 width one, so no drawing is done
if (det == 0) {
goTo(x1, y1);
return;
}
float dx = x1 - x0; float dx = x1 - x0;
float dy = y1 - y0; float dy = y1 - y0;
float len = (float) Math.hypot(dx, dy);
// Compute segment length in the untransformed if (len == 0) {
// coordinate system
float la = (dy*m00 - dx*m10)/det;
float lb = (dy*m01 - dx*m11)/det;
float origLen = (float) Math.hypot(la, lb);
if (origLen == 0) {
// Let the output LineSink deal with cases where dx, dy are 0.
goTo(x1, y1);
return; return;
} }
// The scaling factors needed to get the dx and dy of the // The scaling factors needed to get the dx and dy of the
// transformed dash segments. // transformed dash segments.
float cx = dx / origLen; float cx = dx / len;
float cy = dy / origLen; float cy = dy / len;
while (true) { while (true) {
float leftInThisDashSegment = dash[idx] - phase; float leftInThisDashSegment = dash[idx] - phase;
if (origLen < leftInThisDashSegment) { if (len <= leftInThisDashSegment) {
goTo(x1, y1); curCurvepts[0] = x1;
curCurvepts[1] = y1;
goTo(curCurvepts, 0, 4);
// Advance phase within current dash segment // Advance phase within current dash segment
phase += origLen; phase += len;
return; if (len == leftInThisDashSegment) {
} else if (origLen == leftInThisDashSegment) { phase = 0f;
goTo(x1, y1); idx = (idx + 1) % dash.length;
phase = 0f; dashOn = !dashOn;
idx = (idx + 1) % dash.length; }
dashOn = !dashOn;
return; return;
} }
float dashx, dashy;
float dashdx = dash[idx] * cx; float dashdx = dash[idx] * cx;
float dashdy = dash[idx] * cy; float dashdy = dash[idx] * cy;
if (phase == 0) { if (phase == 0) {
dashx = x0 + dashdx; curCurvepts[0] = x0 + dashdx;
dashy = y0 + dashdy; curCurvepts[1] = y0 + dashdy;
} else { } else {
float p = (leftInThisDashSegment) / dash[idx]; float p = leftInThisDashSegment / dash[idx];
dashx = x0 + p * dashdx; curCurvepts[0] = x0 + p * dashdx;
dashy = y0 + p * dashdy; curCurvepts[1] = y0 + p * dashdy;
} }
goTo(dashx, dashy); goTo(curCurvepts, 0, 4);
len -= leftInThisDashSegment;
// Advance to next dash segment
idx = (idx + 1) % dash.length;
dashOn = !dashOn;
phase = 0;
}
}
private LengthIterator li = null;
// preconditions: curCurvepts must be an array of length at least 2 * type,
// that contains the curve we want to dash in the first type elements
private void somethingTo(int type) {
if (pointCurve(curCurvepts, type)) {
return;
}
if (li == null) {
li = new LengthIterator(4, 0.0001f);
}
li.initializeIterationOnCurve(curCurvepts, type);
origLen -= (dash[idx] - phase); int curCurveoff = 0; // initially the current curve is at curCurvepts[0...type]
float lastSplitT = 0;
float t = 0;
float leftInThisDashSegment = dash[idx] - phase;
while ((t = li.next(leftInThisDashSegment)) < 1) {
if (t != 0) {
Helpers.subdivideAt((t - lastSplitT) / (1 - lastSplitT),
curCurvepts, curCurveoff,
curCurvepts, 0,
curCurvepts, type, type);
lastSplitT = t;
goTo(curCurvepts, 2, type);
curCurveoff = type;
}
// Advance to next dash segment // Advance to next dash segment
idx = (idx + 1) % dash.length; idx = (idx + 1) % dash.length;
dashOn = !dashOn; dashOn = !dashOn;
phase = 0; phase = 0;
leftInThisDashSegment = dash[idx];
}
goTo(curCurvepts, curCurveoff+2, type);
phase += li.lastSegLen();
if (phase >= dash[idx]) {
phase = 0f;
idx = (idx + 1) % dash.length;
dashOn = !dashOn;
}
}
private static boolean pointCurve(float[] curve, int type) {
for (int i = 2; i < type; i++) {
if (curve[i] != curve[i-2]) {
return false;
}
}
return true;
}
// Objects of this class are used to iterate through curves. They return
// t values where the left side of the curve has a specified length.
// It does this by subdividing the input curve until a certain error
// condition has been met. A recursive subdivision procedure would
// return as many as 1<<limit curves, but this is an iterator and we
// don't need all the curves all at once, so what we carry out a
// lazy inorder traversal of the recursion tree (meaning we only move
// through the tree when we need the next subdivided curve). This saves
// us a lot of memory because at any one time we only need to store
// limit+1 curves - one for each level of the tree + 1.
// NOTE: the way we do things here is not enough to traverse a general
// tree; however, the trees we are interested in have the property that
// every non leaf node has exactly 2 children
private static class LengthIterator {
private enum Side {LEFT, RIGHT};
// Holds the curves at various levels of the recursion. The root
// (i.e. the original curve) is at recCurveStack[0] (but then it
// gets subdivided, the left half is put at 1, so most of the time
// only the right half of the original curve is at 0)
private float[][] recCurveStack;
// sides[i] indicates whether the node at level i+1 in the path from
// the root to the current leaf is a left or right child of its parent.
private Side[] sides;
private int curveType;
private final int limit;
private final float ERR;
private final float minTincrement;
// lastT and nextT delimit the current leaf.
private float nextT;
private float lenAtNextT;
private float lastT;
private float lenAtLastT;
private float lenAtLastSplit;
private float lastSegLen;
// the current level in the recursion tree. 0 is the root. limit
// is the deepest possible leaf.
private int recLevel;
private boolean done;
public LengthIterator(int reclimit, float err) {
this.limit = reclimit;
this.minTincrement = 1f / (1 << limit);
this.ERR = err;
this.recCurveStack = new float[reclimit+1][8];
this.sides = new Side[reclimit];
// if any methods are called without first initializing this object on
// a curve, we want it to fail ASAP.
this.nextT = Float.MAX_VALUE;
this.lenAtNextT = Float.MAX_VALUE;
this.lenAtLastSplit = Float.MIN_VALUE;
this.recLevel = Integer.MIN_VALUE;
this.lastSegLen = Float.MAX_VALUE;
this.done = true;
}
public void initializeIterationOnCurve(float[] pts, int type) {
System.arraycopy(pts, 0, recCurveStack[0], 0, type);
this.curveType = type;
this.recLevel = 0;
this.lastT = 0;
this.lenAtLastT = 0;
this.nextT = 0;
this.lenAtNextT = 0;
goLeft(); // initializes nextT and lenAtNextT properly
this.lenAtLastSplit = 0;
if (recLevel > 0) {
this.sides[0] = Side.LEFT;
this.done = false;
} else {
// the root of the tree is a leaf so we're done.
this.sides[0] = Side.RIGHT;
this.done = true;
}
this.lastSegLen = 0;
}
// returns the t value where the remaining curve should be split in
// order for the left subdivided curve to have length len. If len
// is >= than the length of the uniterated curve, it returns 1.
public float next(float len) {
float targetLength = lenAtLastSplit + len;
while(lenAtNextT < targetLength) {
if (done) {
lastSegLen = lenAtNextT - lenAtLastSplit;
return 1;
}
goToNextLeaf();
}
lenAtLastSplit = targetLength;
float t = binSearchForLen(lenAtLastSplit - lenAtLastT,
recCurveStack[recLevel], curveType, lenAtNextT - lenAtLastT, ERR);
// t is relative to the current leaf, so we must make it a valid parameter
// of the original curve.
t = t * (nextT - lastT) + lastT;
if (t >= 1) {
t = 1;
done = true;
}
// even if done = true, if we're here, that means targetLength
// is equal to, or very, very close to the total length of the
// curve, so lastSegLen won't be too high. In cases where len
// overshoots the curve, this method will exit in the while
// loop, and lastSegLen will still be set to the right value.
lastSegLen = len;
return t;
}
public float lastSegLen() {
return lastSegLen;
}
// Returns t such that if leaf is subdivided at t the left
// curve will have length len. leafLen must be the length of leaf.
private static Curve bsc = new Curve();
private static float binSearchForLen(float len, float[] leaf, int type,
float leafLen, float err)
{
assert len <= leafLen;
bsc.set(leaf, type);
float errBound = err*len;
float left = 0, right = 1;
while (left < right) {
float m = (left + right) / 2;
if (m == left || m == right) {
return m;
}
float x = bsc.xat(m);
float y = bsc.yat(m);
float leftLen = Helpers.linelen(leaf[0], leaf[1], x, y);
if (Math.abs(leftLen - len) < errBound) {
return m;
}
if (leftLen < len) {
left = m;
} else {
right = m;
}
}
return left;
}
// go to the next leaf (in an inorder traversal) in the recursion tree
// preconditions: must be on a leaf, and that leaf must not be the root.
private void goToNextLeaf() {
// We must go to the first ancestor node that has an unvisited
// right child.
recLevel--;
while(sides[recLevel] == Side.RIGHT) {
if (recLevel == 0) {
done = true;
return;
}
recLevel--;
}
sides[recLevel] = Side.RIGHT;
System.arraycopy(recCurveStack[recLevel], 0, recCurveStack[recLevel+1], 0, curveType);
recLevel++;
goLeft();
}
// go to the leftmost node from the current node. Return its length.
private void goLeft() {
float len = onLeaf();
if (len >= 0) {
lastT = nextT;
lenAtLastT = lenAtNextT;
nextT += (1 << (limit - recLevel)) * minTincrement;
lenAtNextT += len;
} else {
Helpers.subdivide(recCurveStack[recLevel], 0,
recCurveStack[recLevel+1], 0,
recCurveStack[recLevel], 0, curveType);
sides[recLevel] = Side.LEFT;
recLevel++;
goLeft();
}
}
// this is a bit of a hack. It returns -1 if we're not on a leaf, and
// the length of the leaf if we are on a leaf.
private float onLeaf() {
float polylen = Helpers.polyLineLength(recCurveStack[recLevel], 0, curveType);
float linelen = Helpers.linelen(recCurveStack[recLevel][0], recCurveStack[recLevel][1],
recCurveStack[recLevel][curveType - 2], recCurveStack[recLevel][curveType - 1]);
return (polylen - linelen < ERR || recLevel == limit) ?
(polylen + linelen)/2 : -1;
} }
} }
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
curCurvepts[0] = x0; curCurvepts[1] = y0;
curCurvepts[2] = x1; curCurvepts[3] = y1;
curCurvepts[4] = x2; curCurvepts[5] = y2;
curCurvepts[6] = x3; curCurvepts[7] = y3;
somethingTo(8);
}
public void close() { @Override
public void quadTo(float x1, float y1, float x2, float y2) {
curCurvepts[0] = x0; curCurvepts[1] = y0;
curCurvepts[2] = x1; curCurvepts[3] = y1;
curCurvepts[4] = x2; curCurvepts[5] = y2;
somethingTo(6);
}
public void closePath() {
lineTo(sx, sy); lineTo(sx, sy);
if (firstDashOn) { if (firstSegidx > 0) {
output.lineTo(sx1, sy1); if (!dashOn || needsMoveTo) {
out.moveTo(sx, sy);
}
emitFirstSegments();
} }
moveTo(sx, sy);
} }
public void end() { public void pathDone() {
output.end(); if (firstSegidx > 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
out.pathDone();
}
@Override
public long getNativeConsumer() {
throw new InternalError("Dasher does not use a native consumer");
} }
} }
此差异已折叠。
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
package sun.java2d.pisces; package sun.java2d.pisces;
import java.util.Arrays;
/** /**
* An object used to cache pre-rendered complex paths. * An object used to cache pre-rendered complex paths.
* *
...@@ -32,115 +34,153 @@ package sun.java2d.pisces; ...@@ -32,115 +34,153 @@ package sun.java2d.pisces;
*/ */
public final class PiscesCache { public final class PiscesCache {
int bboxX0, bboxY0, bboxX1, bboxY1; final int bboxX0, bboxY0, bboxX1, bboxY1;
// rowAARLE[i] holds the encoding of the pixel row with y = bboxY0+i.
// The format of each of the inner arrays is: rowAARLE[i][0,1] = (x0, n)
// where x0 is the first x in row i with nonzero alpha, and n is the
// number of RLE entries in this row. rowAARLE[i][j,j+1] for j>1 is
// (val,runlen)
final int[][] rowAARLE;
// RLE encodings are added in increasing y rows and then in increasing
// x inside those rows. Therefore, at any one time there is a well
// defined position (x,y) where a run length is about to be added (or
// the row terminated). x0,y0 is this (x,y)-(bboxX0,bboxY0). They
// are used to get indices into the current tile.
private int x0 = Integer.MIN_VALUE, y0 = Integer.MIN_VALUE;
// touchedTile[i][j] is the sum of all the alphas in the tile with
// y=i*TILE_SIZE+bboxY0 and x=j*TILE_SIZE+bboxX0.
private final int[][] touchedTile;
static final int TILE_SIZE_LG = 5;
static final int TILE_SIZE = 1 << TILE_SIZE_LG; // 32
private static final int INIT_ROW_SIZE = 8; // enough for 3 run lengths
PiscesCache(int minx, int miny, int maxx, int maxy) {
assert maxy >= miny && maxx >= minx;
bboxX0 = minx;
bboxY0 = miny;
bboxX1 = maxx + 1;
bboxY1 = maxy + 1;
// we could just leave the inner arrays as null and allocate them
// lazily (which would be beneficial for shapes with gaps), but we
// assume there won't be too many of those so we allocate everything
// up front (which is better for other cases)
rowAARLE = new int[bboxY1 - bboxY0 + 1][INIT_ROW_SIZE];
x0 = 0;
y0 = -1; // -1 makes the first assert in startRow succeed
// the ceiling of (maxy - miny + 1) / TILE_SIZE;
int nyTiles = (maxy - miny + TILE_SIZE) >> TILE_SIZE_LG;
int nxTiles = (maxx - minx + TILE_SIZE) >> TILE_SIZE_LG;
touchedTile = new int[nyTiles][nxTiles];
}
byte[] rowAARLE; void addRLERun(int val, int runLen) {
int alphaRLELength; if (runLen > 0) {
addTupleToRow(y0, val, runLen);
if (val != 0) {
// the x and y of the current row, minus bboxX0, bboxY0
int tx = x0 >> TILE_SIZE_LG;
int ty = y0 >> TILE_SIZE_LG;
int tx1 = (x0 + runLen - 1) >> TILE_SIZE_LG;
// while we forbid rows from starting before bboxx0, our users
// can still store rows that go beyond bboxx1 (although this
// shouldn't happen), so it's a good idea to check that i
// is not going out of bounds in touchedTile[ty]
if (tx1 >= touchedTile[ty].length) {
tx1 = touchedTile[ty].length - 1;
}
if (tx <= tx1) {
int nextTileXCoord = (tx + 1) << TILE_SIZE_LG;
if (nextTileXCoord > x0+runLen) {
touchedTile[ty][tx] += val * runLen;
} else {
touchedTile[ty][tx] += val * (nextTileXCoord - x0);
}
tx++;
}
// don't go all the way to tx1 - we need to handle the last
// tile as a special case (just like we did with the first
for (; tx < tx1; tx++) {
// try {
touchedTile[ty][tx] += (val << TILE_SIZE_LG);
// } catch (RuntimeException e) {
// System.out.println("x0, y0: " + x0 + ", " + y0);
// System.out.printf("tx, ty, tx1: %d, %d, %d %n", tx, ty, tx1);
// System.out.printf("bboxX/Y0/1: %d, %d, %d, %d %n",
// bboxX0, bboxY0, bboxX1, bboxY1);
// throw e;
// }
}
// they will be equal unless x0>>TILE_SIZE_LG == tx1
if (tx == tx1) {
int lastXCoord = Math.min(x0 + runLen, (tx + 1) << TILE_SIZE_LG);
int txXCoord = tx << TILE_SIZE_LG;
touchedTile[ty][tx] += val * (lastXCoord - txXCoord);
}
}
x0 += runLen;
}
}
int[] rowOffsetsRLE; void startRow(int y, int x) {
int[] minTouched; // rows are supposed to be added by increasing y.
int alphaRows; assert y - bboxY0 > y0;
assert y <= bboxY1; // perhaps this should be < instead of <=
private PiscesCache() {} y0 = y - bboxY0;
// this should be a new, uninitialized row.
assert rowAARLE[y0][1] == 0;
public static PiscesCache createInstance() { x0 = x - bboxX0;
return new PiscesCache(); assert x0 >= 0 : "Input must not be to the left of bbox bounds";
}
private static final float ROWAA_RLE_FACTOR = 1.5f; // the way addTupleToRow is implemented it would work for this but it's
private static final float TOUCHED_FACTOR = 1.5f; // not a good idea to use it because it is meant for adding
private static final int MIN_TOUCHED_LEN = 64; // RLE tuples, not the first tuple (which is special).
rowAARLE[y0][0] = x;
private void reallocRowAARLE(int newLength) { rowAARLE[y0][1] = 2;
if (rowAARLE == null) {
rowAARLE = new byte[newLength];
} else if (rowAARLE.length < newLength) {
int len = Math.max(newLength,
(int)(rowAARLE.length*ROWAA_RLE_FACTOR));
byte[] newRowAARLE = new byte[len];
System.arraycopy(rowAARLE, 0, newRowAARLE, 0, rowAARLE.length);
rowAARLE = newRowAARLE;
}
} }
private void reallocRowInfo(int newHeight) { int alphaSumInTile(int x, int y) {
if (minTouched == null) { x -= bboxX0;
int len = Math.max(newHeight, MIN_TOUCHED_LEN); y -= bboxY0;
minTouched = new int[len]; return touchedTile[y>>TILE_SIZE_LG][x>>TILE_SIZE_LG];
rowOffsetsRLE = new int[len];
} else if (minTouched.length < newHeight) {
int len = Math.max(newHeight,
(int)(minTouched.length*TOUCHED_FACTOR));
int[] newMinTouched = new int[len];
int[] newRowOffsetsRLE = new int[len];
System.arraycopy(minTouched, 0, newMinTouched, 0,
alphaRows);
System.arraycopy(rowOffsetsRLE, 0, newRowOffsetsRLE, 0,
alphaRows);
minTouched = newMinTouched;
rowOffsetsRLE = newRowOffsetsRLE;
}
} }
void addRLERun(byte val, int runLen) { int minTouched(int rowidx) {
reallocRowAARLE(alphaRLELength + 2); return rowAARLE[rowidx][0];
rowAARLE[alphaRLELength++] = val;
rowAARLE[alphaRLELength++] = (byte)runLen;
} }
void startRow(int y, int x0, int x1) { int rowLength(int rowidx) {
if (alphaRows == 0) { return rowAARLE[rowidx][1];
bboxY0 = y;
bboxY1 = y+1;
bboxX0 = x0;
bboxX1 = x1+1;
} else {
if (bboxX0 > x0) bboxX0 = x0;
if (bboxX1 < x1 + 1) bboxX1 = x1 + 1;
while (bboxY1++ < y) {
reallocRowInfo(alphaRows+1);
minTouched[alphaRows] = 0;
// Assuming last 2 entries in rowAARLE are 0,0
rowOffsetsRLE[alphaRows] = alphaRLELength-2;
alphaRows++;
}
}
reallocRowInfo(alphaRows+1);
minTouched[alphaRows] = x0;
rowOffsetsRLE[alphaRows] = alphaRLELength;
alphaRows++;
} }
public synchronized void dispose() { private void addTupleToRow(int row, int a, int b) {
rowAARLE = null; int end = rowAARLE[row][1];
alphaRLELength = 0; rowAARLE[row] = Helpers.widenArray(rowAARLE[row], end, 2);
rowAARLE[row][end++] = a;
minTouched = null; rowAARLE[row][end++] = b;
rowOffsetsRLE = null; rowAARLE[row][1] = end;
alphaRows = 0;
bboxX0 = bboxY0 = bboxX1 = bboxY1 = 0;
} }
public void print(java.io.PrintStream out) { @Override
synchronized (out) { public String toString() {
out.println("bbox = ["+ String ret = "bbox = ["+
bboxX0+", "+bboxY0+" => "+ bboxX0+", "+bboxY0+" => "+
bboxX1+", "+bboxY1+"]"); bboxX1+", "+bboxY1+"]\n";
for (int[] row : rowAARLE) {
out.println("alphRLELength = "+alphaRLELength); if (row != null) {
ret += ("minTouchedX=" + row[0] +
for (int y = bboxY0; y < bboxY1; y++) { "\tRLE Entries: " + Arrays.toString(
int i = y-bboxY0; Arrays.copyOfRange(row, 2, row[1])) + "\n");
out.println("row["+i+"] == {"+ } else {
"minX = "+minTouched[i]+ ret += "[]\n";
", off = "+rowOffsetsRLE[i]+"}"); }
}
for (int i = 0; i < alphaRLELength; i += 2) {
out.println("rle["+i+"] = "+
(rowAARLE[i+1]&0xff)+" of "+(rowAARLE[i]&0xff));
} }
} return ret;
} }
} }
...@@ -25,40 +25,54 @@ ...@@ -25,40 +25,54 @@
package sun.java2d.pisces; package sun.java2d.pisces;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import sun.java2d.pipe.AATileGenerator; import sun.java2d.pipe.AATileGenerator;
public class PiscesTileGenerator implements AATileGenerator { public final class PiscesTileGenerator implements AATileGenerator {
public static final int TILE_SIZE = 32; public static final int TILE_SIZE = PiscesCache.TILE_SIZE;
// perhaps we should be using weak references here, but right now
// that's not necessary. The way the renderer is, this map will
// never contain more than one element - the one with key 64, since
// we only do 8x8 supersampling.
private static final Map<Integer, byte[]> alphaMapsCache = new
ConcurrentHashMap<Integer, byte[]>();
PiscesCache cache; PiscesCache cache;
int x, y; int x, y;
int maxalpha; final int maxalpha;
private final int maxTileAlphaSum;
// The alpha map used by this object (taken out of our map cache) to convert
// pixel coverage counts gotten from PiscesCache (which are in the range
// [0, maxalpha]) into alpha values, which are in [0,256).
byte alphaMap[]; byte alphaMap[];
public PiscesTileGenerator(PiscesCache cache, int maxalpha) { public PiscesTileGenerator(Renderer r, int maxalpha) {
this.cache = cache; this.cache = r.getCache();
this.x = cache.bboxX0; this.x = cache.bboxX0;
this.y = cache.bboxY0; this.y = cache.bboxY0;
this.alphaMap = getAlphaMap(maxalpha); this.alphaMap = getAlphaMap(maxalpha);
this.maxalpha = maxalpha; this.maxalpha = maxalpha;
this.maxTileAlphaSum = TILE_SIZE*TILE_SIZE*maxalpha;
} }
static int prevMaxAlpha; private static byte[] buildAlphaMap(int maxalpha) {
static byte prevAlphaMap[]; byte[] alMap = new byte[maxalpha+1];
int halfmaxalpha = maxalpha>>2;
for (int i = 0; i <= maxalpha; i++) {
alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
}
return alMap;
}
public synchronized static byte[] getAlphaMap(int maxalpha) { public static byte[] getAlphaMap(int maxalpha) {
if (maxalpha != prevMaxAlpha) { if (!alphaMapsCache.containsKey(maxalpha)) {
prevAlphaMap = new byte[maxalpha+300]; alphaMapsCache.put(maxalpha, buildAlphaMap(maxalpha));
int halfmaxalpha = maxalpha>>2;
for (int i = 0; i <= maxalpha; i++) {
prevAlphaMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
}
for (int i = maxalpha; i < prevAlphaMap.length; i++) {
prevAlphaMap[i] = (byte) 255;
}
prevMaxAlpha = maxalpha;
} }
return prevAlphaMap; return alphaMapsCache.get(maxalpha);
} }
public void getBbox(int bbox[]) { public void getBbox(int bbox[]) {
...@@ -96,53 +110,24 @@ public class PiscesTileGenerator implements AATileGenerator { ...@@ -96,53 +110,24 @@ public class PiscesTileGenerator implements AATileGenerator {
* value for partial coverage of the tile * value for partial coverage of the tile
*/ */
public int getTypicalAlpha() { public int getTypicalAlpha() {
if (true) return 0x80; int al = cache.alphaSumInTile(x, y);
// Decode run-length encoded alpha mask data // Note: if we have a filled rectangle that doesn't end on a tile
// The data for row j begins at cache.rowOffsetsRLE[j] // border, we could still return 0xff, even though al!=maxTileAlphaSum
// and is encoded as a set of 2-byte pairs (val, runLen) // This is because if we return 0xff, our users will fill a rectangle
// terminated by a (0, 0) pair. // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),
// and height min(TILE_SIZE,bboxY1-y), which is what should happen.
int x0 = this.x; // However, to support this, we would have to use 2 Math.min's
int x1 = x0 + TILE_SIZE; // and 2 multiplications per tile, instead of just 2 multiplications
int y0 = this.y; // to compute maxTileAlphaSum. The savings offered would probably
int y1 = y0 + TILE_SIZE; // not be worth it, considering how rare this case is.
if (x1 > cache.bboxX1) x1 = cache.bboxX1; // Note: I have not tested this, so in the future if it is determined
if (y1 > cache.bboxY1) y1 = cache.bboxY1; // that it is worth it, it should be implemented. Perhaps this method's
y0 -= cache.bboxY0; // interface should be changed to take arguments the width and height
y1 -= cache.bboxY0; // of the current tile. This would eliminate the 2 Math.min calls that
// would be needed here, since our caller needs to compute these 2
int ret = -1; // values anyway.
for (int cy = y0; cy < y1; cy++) { return (al == 0x00 ? 0x00 :
int pos = cache.rowOffsetsRLE[cy]; (al == maxTileAlphaSum ? 0xff : 0x80));
int cx = cache.minTouched[cy];
if (cx > x0) {
if (ret > 0) return 0x80;
ret = 0x00;
}
while (cx < x1) {
int runLen = cache.rowAARLE[pos + 1] & 0xff;
if (runLen == 0) {
if (ret > 0) return 0x80;
ret = 0x00;
break;
}
cx += runLen;
if (cx > x0) {
int val = cache.rowAARLE[pos] & 0xff;
if (ret != val) {
if (ret < 0) {
if (val != 0x00 && val != maxalpha) return 0x80;
ret = val;
} else {
return 0x80;
}
}
}
pos += 2;
}
}
return ret;
} }
/** /**
...@@ -179,22 +164,24 @@ public class PiscesTileGenerator implements AATileGenerator { ...@@ -179,22 +164,24 @@ public class PiscesTileGenerator implements AATileGenerator {
int idx = offset; int idx = offset;
for (int cy = y0; cy < y1; cy++) { for (int cy = y0; cy < y1; cy++) {
int pos = cache.rowOffsetsRLE[cy]; int[] row = cache.rowAARLE[cy];
int cx = cache.minTouched[cy]; assert row != null;
int cx = cache.minTouched(cy);
if (cx > x1) cx = x1; if (cx > x1) cx = x1;
if (cx > x0) { for (int i = x0; i < cx; i++) {
//System.out.println("L["+(cx-x0)+"]"); tile[idx++] = 0x00;
for (int i = x0; i < cx; i++) {
tile[idx++] = 0x00;
}
} }
while (cx < x1) {
int pos = 2;
while (cx < x1 && pos < row[1]) {
byte val; byte val;
int runLen = 0; int runLen = 0;
assert row[1] > 2;
try { try {
val = alphaMap[cache.rowAARLE[pos] & 0xff]; val = alphaMap[row[pos]];
runLen = cache.rowAARLE[pos + 1] & 0xff; runLen = row[pos + 1];
assert runLen > 0;
} catch (RuntimeException e0) { } catch (RuntimeException e0) {
System.out.println("maxalpha = "+maxalpha); System.out.println("maxalpha = "+maxalpha);
System.out.println("tile["+x0+", "+y0+ System.out.println("tile["+x0+", "+y0+
...@@ -202,14 +189,12 @@ public class PiscesTileGenerator implements AATileGenerator { ...@@ -202,14 +189,12 @@ public class PiscesTileGenerator implements AATileGenerator {
System.out.println("cx = "+cx+", cy = "+cy); System.out.println("cx = "+cx+", cy = "+cy);
System.out.println("idx = "+idx+", pos = "+pos); System.out.println("idx = "+idx+", pos = "+pos);
System.out.println("len = "+runLen); System.out.println("len = "+runLen);
cache.print(System.out); System.out.print(cache.toString());
e0.printStackTrace(); e0.printStackTrace();
System.exit(1); System.exit(1);
return; return;
} }
if (runLen == 0) {
break;
}
int rx0 = cx; int rx0 = cx;
cx += runLen; cx += runLen;
int rx1 = cx; int rx1 = cx;
...@@ -228,7 +213,7 @@ public class PiscesTileGenerator implements AATileGenerator { ...@@ -228,7 +213,7 @@ public class PiscesTileGenerator implements AATileGenerator {
System.out.println("idx = "+idx+", pos = "+pos); System.out.println("idx = "+idx+", pos = "+pos);
System.out.println("rx0 = "+rx0+", rx1 = "+rx1); System.out.println("rx0 = "+rx0+", rx1 = "+rx1);
System.out.println("len = "+runLen); System.out.println("len = "+runLen);
cache.print(System.out); System.out.print(cache.toString());
e.printStackTrace(); e.printStackTrace();
System.exit(1); System.exit(1);
return; return;
...@@ -265,4 +250,4 @@ public class PiscesTileGenerator implements AATileGenerator { ...@@ -265,4 +250,4 @@ public class PiscesTileGenerator implements AATileGenerator {
* No further calls will be made on this instance. * No further calls will be made on this instance.
*/ */
public void dispose() {} public void dispose() {}
} }
\ No newline at end of file
...@@ -145,7 +145,7 @@ public class FormatData_fr extends ListResourceBundle { ...@@ -145,7 +145,7 @@ public class FormatData_fr extends ListResourceBundle {
"{1} {0}" // date-time pattern "{1} {0}" // date-time pattern
} }
}, },
{ "DateTimePatternChars", "GaMjkHmsSEDFwWahKzZ" }, { "DateTimePatternChars", "GaMjkHmsSEDFwWxhKzZ" },
}; };
} }
} }
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册