| Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. |
| DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| |
| This code is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License version 2 only, as |
| published by the Free Software Foundation. |
| |
| This code is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| version 2 for more details (a copy is included in the LICENSE file that |
| accompanied this code). |
| |
| You should have received a copy of the GNU General Public License version |
| 2 along with this work; if not, write to the Free Software Foundation, |
| Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| |
| Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| or visit www.oracle.com if you need additional information or have any |
| questions. |
| |
| |
| ABOUT |
| |
| Once published, it is impossible to add methods to an interface without |
| breaking existing implementations (specifically, adding a method to an |
| interface is not a source-compatible change). The longer the time since a |
| library has been published, the more likely it is that this restriction will |
| cause grief for its maintainers. |
| |
| The addition of closures to the Java language in JDK 8 place additional stress |
| on the aging Collection interfaces; one of the most significant benefits of |
| closures is that it enables the development of more powerful libraries. It |
| would be disappointing to add a language feature that enables better libraries |
| while at the same time not extending the core libraries to take advantage of |
| that feature. |
| |
| A mechanism for adding new methods to existing interfaces is proposed, which is |
| called virtual extension (or default) methods. Existing interfaces can be |
| augmented without compromising backward compatibility by adding extension |
| methods to the interface, whose declaration would contain instructions for |
| finding the default implementation in the event that implementers do not |
| provide a method body. A key characteristic of extension methods is that they |
| are virtual methods just like other interface methods, but provide a default |
| implementation in the event that the implementing class does not provide a |
| method body. |
| |
| VM support is necessary to implement virtual extension methods. |
| |
| |
| OVERVIEW |
| |
| The test suite is organized in the following manner. |
| |
| The tests rely on a framework to generate class hierarchies and tests |
| directly in bytecode from a pseudo-code in Java. Pseudo-code is written |
| using builder pattern and fluent coding style. |
| |
| The framework is located in src/vm/runtime/defmeth/shared and divided into |
| /data and /builder sections. |
| |
| As an example, the following code: |
| |
| TestBuilder b = factory.getBuilder(); |
| |
| Interface I = b.intf("I") |
| .defaultMethod("m", "()I").returns(1).build() |
| .build(); |
| |
| ConcreteClass C = b.clazz("C").implement(I) |
| .concreteMethod("m", "()I").returns(2).build() |
| .build(); |
| |
| b.test().callSite(I, C, "m", "()I").returns(2).done() |
| .test().callSite(C, C, "m", "()I").returns(2).done() |
| |
| .run(); |
| |
| translates into bytecode equivalent of: |
| |
| 2-class hierarchy: |
| |
| interface I { |
| int m() default { return 1; } |
| } |
| |
| class C implements I { |
| public int m() { return 2; } |
| } |
| |
| and 2 tests: |
| |
| Test1_I_C_m { |
| static void test() { |
| I i = new C(); |
| if (i.m() != 2) throw new TestFailure(); |
| } |
| } |
| |
| Test2_C_C_m { |
| static void test() { |
| C c = new C(); |
| if (c.m() != 2) throw new TestFailure(); |
| } |
| } |
| |
| TestBuilder.run() calls Test1_I_C_m.test() and Test2_C_C_m.test() and |
| performs failure reporting, if necessary. |
| |
| All tests are located in src/vm/runtime/defmeth and are grouped according |
| to the area they excercise. The test groups are: |
| - AccessibilityFlagsTest |
| - BasicTest |
| - ConflictingDefaultsTest |
| - DefaultVsAbstractTest |
| - MethodResolutionTest |
| - ObjectMethodOverridesTest |
| - PrivateMethodsTest |
| - RedefineTest |
| - StaticMethodsTest |
| - StressTest |
| - SuperCallTest |
| |
| Each test group can be executed in different modes. For each mode there's a |
| corresponding scenario in src/vm/runtime/defmeth/scenarios. |
| |
| Scenarios are organized in the following manner: |
| |
| .../scenarios/[test_group]_[majorVer]_[methodFlags]_[invocationType]_[shouldRedefine] |
| |
| where |
| |
| majorVer - major version of class files for generated concrete classes |
| values: ver49, ver50, ver51, ver52 |
| |
| methodFlags - additional access flags for methods in generated classes |
| values: |
| none == no additional flags |
| sync == ACC_SYNCHRONIZED |
| strict == ACC_STRICT |
| syncstrict == ACC_SYNCHRONIZED | ACC_STRICT |
| |
| invocationType - how methods in test hiearchies are invoked during testing |
| values: |
| direct - using invoke* bytecodes |
| reflect - using Reflection API |
| invoke - using invokedynamic & java.lang.invoke API (MethodHandles/JSR292) |
| |
| redefine - whether to preload and redefine classes before running individual tests |
| values: redefine, noredefine |
| |
| testGroup - name of test group being used |
| values: BasicTests/BridgeMethod/etc |
| |
| |
| STRESS TESTING |
| |
| Stress test differs from other scenarios - it has only 2 modes: redefine and noredefine. |
| |
| Stress scenario is the following: |
| - in multiple threads (5 by default)... |
| - ... continuously run random vm.runtime.defmeth.* tests ... |
| - ... in random configurations ... |
| - ... until predefined period of time is over... |
| - ... or any failures occured. |
| |
| |
| HOW TO RUN |
| |
| Directly from command-line: |
| |
| $ java -cp ${VMTESTBASE}/bin/classes vm.runtime.defmeth.shared.DefMethTest |
| |
| Specify testing mode: |
| -flags <int> |
| additional access flags on default methods (default: 0) |
| |
| -ver <int> |
| class file major version (default: 52) |
| |
| -redefine <boolean> |
| redefine classes during execution (default: false) |
| |
| -mode [direct|reflect|invoke] |
| specify method invocation mechanism (default: direct): |
| - direct - invoke* instructions in bytecode |
| - reflect - Reflection API |
| - invoke - invokedynamic & MethodHandle.invoke* |
| |
| -execMode [DIRECT|REFLECTION|INVOKE_EXACT|INVOKE_GENERIC|INVOKE_WITH_ARGS|INDY] |
| specify concrete execution mode |
| |
| Execution-specific flags: |
| -list <boolean> |
| list available tests |
| |
| -filter <regex> |
| filter tests by name |
| (default: .* ) |
| |
| If you run tests directly from command line, in order to make "-redefine true", |
| StressTest or RedefineTest work, additional steps are necessary: |
| add -agentlib:redefineClasses to JVM options |
| set correct LD_LIBRARY_PATH: |
| LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${VM_TESTBASE}/bin/lib/${PLATFORM}/vm/runtime/defmeth/shared/ |
| |
| Also, it is possible to run any test group directly: |
| |
| $ java -cp ${VMTESTBASE}/bin/classes vm.runtime.defmeth.BasicTest |
| |
| StressTest has some specific options: |
| -stressTime <long> |
| Stress execution time in seconds (default: 60) |
| |
| -stressThreadsFactor <int> |
| Stress threads factor (default: 1) |
| |
| -seed <int> |
| force deterministic behavior (default: 0) |
| |
| -redefine <boolean> |
| use scenarios w/ class redefinition (default: false) |
| |
| -ver <int> |
| minimum class file version to be used in the tests (default: 49) |
| |
| -ignoreTestFailures |
| ignore failures of individual tests |
| |
| To simplify failure analysis, the framework has some additional flags to produce |
| diagnostics output: |
| |
| -Dvm.runtime.defmeth.printTests |
| print pseudo-code for each test; |
| |
| -Dvm.runtime.defmeth.printAssembly |
| print bytecode assembly for all generated class files; |
| |
| -Dvm.runtime.defmeth.printASMify |
| print "asmified" version of generated class files; |
| very useful when preparing reduced test cases. |
| |
| -Dvm.runtime.defmeth.dumpClasses |
| dump class files under DUMP_CLASS_FILES in <test_name> folder |
| |
| -Dvm.runtime.defmeth.printStackTrace |
| print full stack traces for all errors and test failures |
| |
| -Dvm.runtime.defmeth.traceClassRedefinition |
| trace class redefinition during testing |
| |
| LINKS |
| |
| [1] "Design and Implementation of Default Methods in Hotspot JVM", by Keith McGuigan, 09/18/2012 |
| http://cr.openjdk.java.net/~kamg/default_methods_in_hotspot.txt |
| |
| [2] "Featherweight Defenders: A formal model for virtual extension methods in Java", by Brian Goetz, Robert Field, 03/27/2012 |
| http://cr.openjdk.java.net/~briangoetz/lambda/featherweight-defenders.pdf |
| |
| [3] "Interface evolution via virtual extension methods", by Brian Goetz, 4th draft, 06/2011 |
| http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf |