invokedynamic指令
1.http://blogs.oracle.com/jrose/entry/a_modest_tool_for_writing 翻译
a modest tool for writing JSR 292 code
By John.Rose-Oracle on Nov 15, 2010
由John.Rose在2010年11月15日编写了一个用于开发满足JSR 292规范代码的简单工具
An earlier version of JSR 292 included some syntax support in the Java language for issuing invokedynamic instructions. This is no longer the case in JDK 7. Such support, if it shows up at all, will be dependent on Project Lambda, and that is after JDK 7.
JSR 292规范早期版本包括一些对java语言中invokeDynamic指令语法支持。在JDK7中不再这样,它究竟是否应该支持invokeDynamic指令,完全依赖于Lambda项目,然而
在JDK7中并没有实现对Lambda表达式的支持。
(By the way, for a larger example of the syntax support in action, check out this hand-transformed benchmark code for Rhino. This was part of the exploratory work described in my JavaOne talk.)
顺便说一下,对于一个正在进行中的语法支持更大的示例,查看手动转换的基准代码,这是我的JavaOne演讲中描述的探索性工作
Well, that’s the bad news. But here’s a bit of good news: I have hacked together a classfile transformer, named “indify”, which is able to pattern-match some byte-compiled Java programs, and transform them into equivalent programs which use JSR 292 bytecode features. It can be used to generate invokedynamic instructions, including those with the new bootstrap method argument format. The transformer can also generate “ldc” instructions for MethodHandle and MethodType constants. (Such constants are a little-known feature of JSR 292.)
好吧!那是一个坏消息。但是这里也有一些好消息:我已经设计了一个类文件转换器,命名为“indify”,“indify”能够模式匹配一些已经编译的java字节码程序,并且
能够将他们转换成等价的满足JSR 292规范二进制特征的程序。它能够被用于生成invokedynamic指令,包含哪些使用新的引导方法参数格式。这个转换器也能够
为MethodHandle和MethodType常量生成“ldc”指令。(MethodHandle和MethodType常量是JSR 292规范中鲜为人知的特性)
The code for “indify” is in a Mercurial repository on Kenai.com. It is all in a single file of less than 1500 lines. (Update: More recent versions are slightly larger.)
“indify”代码在Kenai.com上的 Mercurial版本库中。这一切都在一个不超过1500行的文件中。
Of course, the creation of semantics-preserving program transformations is often difficult. And in this case, the transformation is slightly uphill, more like a decompiler than a compiler. Lower-level expressions are turned into simpler, higher-level JSR 292 operations.
当然,创建一个保持语义性的程序是困难的。在这种情况下,转换有点艰难,,更像一个反编译器的编译器。低层表达式变成更简单、更高级的JSR 292的操作
To make the project workable and small, I cut corners. The tool transforms only a limited set of Java programs. In order to make it work for you, you have to prepare stylized, stereotyped Java programs, which make it very clear where you are expecting to perform the transformations.
为了使项目切实可行和小,我偷工减料。这个工具仅仅转换一组有限的java程序。为了使它为你工作,你需要准备按固定格式编写的Java程序,这使得
你期望在什么地方执行转换非常清楚
The javadoc in the source code gives some rules for preparing your code. There is also a small example file. The basic trick works like this. Suppose you have a method that you want to create a method handle constant on, like this one:
在源码中java帮助文档为你编写代码提供一些规则。也有一些小的示例文件。
假如你你想在一个方法上面创建一个方法句柄常量,像这样:
public static Integer adder(Integer x, Integer y) { return x + y; }
Getting the method handle requires an expression that looks like this:
获取一个方法句柄需要像这样的表达式:
lookup().findStatic(Example.class, "adder",
fromMethodDescriptorString("(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;", null));
Suppose you want to transform that expression into a simple method handle constant. Perhaps in Java with closures you'll be able to write #adder but for now, put the noxious expression into a small private method like this one:
假如你想将这个表达式转换成一个简单的方法句柄常量。或许在java里面使用包庇你将能够写#addr。但是现在,把恶意性表达式放入小的私有方法里面,像这样:
private static MethodHandle MH_adder() throws NoAccessException {
return lookup().findStatic(Example.class, "adder",
fromMethodDescriptorString("(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;", null));
}
The name must begin with the string MH_; the rest of the name is arbitrary. Call the function whereever you like (within the same file). Compile the Java code, and run “indify” on the resulting class file. The constant implied by MH_adder will be appended to the class file constant pool, and all such calls to the method will be transformed into ldc instructions.
The pattern for invokedynamic is more complex, and builds on top of simpler method handle and method type constant expressions. Here is an example:
名字必须以字符串MH_为前缀;名字的剩余部分可以随便写。无论你想在(同一个文件中)哪里调用这个功能。编译Java代码,在生成class文件上运行“indify”。
这个常量将被附加到class文件常量池。所有这个方法的调用将被转换成“ldc”指令。
invokedynamic模式更加复杂,建立在简单方法句柄和方法类型常量表达式之上。这里有个例子
private static MethodHandle INDY_tester() throws Throwable {
CallSite cs = (CallSite) MH_mybsm().invokeWithArguments(lookup(), "myindyname",
fromMethodDescriptorString("(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;", null));
return cs.dynamicInvoker();
}
This code assumes that your bootstrap method is provided by a private constant method MH_mybsm. The factored method INDY_tester calls the bootstrap method on the standard arguments (class scope, name, and type), obtains the call site, and returns the site’s invoker method handle. (This is a late-bound alias for the target, which follows the call site target as it changes.) The caller of INDY_tester is required to immediately invoke the result, like this:
这段代码假定你的启动类方法被私有的常量方法MH_mybsm提供。INDY_tester方法使用标准的参数(class scope, name, and type)调用启动方法,获取调用点,并且返回
点的调用者方法句柄。
System.out.println((Integer) INDY_tester().invokeExact((Integer)40,(Integer)2));
That invokeExact call is part of the built-in semantics of invokedynamic. The “indify” tool tranforms the two calls INDY_tester and invokeExact into a single invokedynamic instruction. The static linkage information for this instruction (which is obtained by inspecting the contents of INDY_tester) is appended to the class file constant pool, as in the previous example.
invokeExact调用是invokedynamic内嵌语义的部分。“indify”工具将2个方法(INDY_tester和invokeExact)调用转换成单一的invokedynamic指令。invokedynamic指令(
这个指令是通过检查INDY_tester的内容获取)的静态链接信息被附加到class文件的常量池,正如先前的示例。
Here is a pre-built distribution. For now, you should throw the switch “--transitionalJSR292”, to make it emit the older format (tag 17) of CONSTANT_InvokeDynamic. (The newer format, tag 18, is not widely available yet.) [12/23 Update: Build b123 of OpenJDK 7 supports tag 18, and contains a version of indify.] To find out about the bytecode instructions, you can always look here.
For large scale use, this tool is a non-starter. For medium sized projects, it is at best a dead end. Real language implementations will need to use a real bytecode backend, like ASM. A small tool like “indify&rdquo is not robust or user-friendly enough for medium to large jobs. And, I’m not planning on supporting it, or developing it much further. Actually, I would be sad to see it get much bigger or more complex.
But “indify” may be useful for small inputs, in cases where the result of the transformation is easy to verify. For example, if you are experimenting with small bytecode examples, or writing or teaching about them, this is a way to make your work be readable as Java code, instead of pseudocode or bytecode assembly language.
2.INDY工具的使用:如何生成invokedynamic指令
1.项目的目录结构如下:
2.编写Example
/*
* 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 indify;
import java.lang.invoke.*;
import java.util.*;
import static java.lang.invoke.MethodType.*;
import static java.lang.invoke.MethodHandles.*;
/* example class file to transform with indify */
class Example {
private static final boolean VERBOSE
= Boolean.getBoolean("indify.Example.VERBOSE");
private static final boolean ALLOW_UNTRANSFORMED
= Boolean.getBoolean("indify.Example.ALLOW_UNTRANSFORMED");
private static void verbose(Object x) {
if (!VERBOSE) return;
System.out.println(x);
}
private static void verboseUntransformed(Object x) {
// calls to this guy should be transformed away
if (x == null && ALLOW_UNTRANSFORMED) return;
x = "[UNTRANSFORMED] " + (x == null ? "call to indified method": x);
if (!ALLOW_UNTRANSFORMED) throw new RuntimeException(x.toString());
verbose(x);
}
public static Integer adder(Integer x, Integer y) { return x + y; }
public static Object mybsm(Object x, Object y, Object z) throws Throwable {
verbose("mybsm"+Arrays.asList(x,y,z));
MethodHandle mh = MH_adder();
if (!z.equals(mh.type())) {
verbose("spreading "+mh+" to "+z);
mh = MH_adder().asSpreader(Object[].class, 2).asType(methodType(Object.class, Object[].class));
}
verbose("mh="+mh);
return new ConstantCallSite(mh);
}
// exercise:
public static void main(String... av) throws Throwable {
System.out.println("MT = "+MT_gen1());
System.out.println("MH = "+MH_adder());
System.out.println("adder(1,2) = "+MH_adder().invokeWithArguments(1, 2));
System.out.print("calling indy: ");
if (true)
System.out.println((Integer) INDY_tester().invokeExact((Integer)40,(Integer)2));
else
System.out.println(INDY_tester().invokeExact(new Object[]{40,2}));
}
// declarations:
private static MethodType MT_gen1() {
verboseUntransformed(null);
return methodType(Object.class, Object.class);
}
private static MethodType MT_gen3() {
// fromMethodDescriptorString("(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null);
verboseUntransformed(null);
return methodType(Object.class, Object.class, Object.class, Object.class);
}
private static MethodHandle MH_adder() throws ReflectiveOperationException {
verboseUntransformed(null);
return lookup().findStatic(Example.class, "adder", methodType(Integer.class, Integer.class, Integer.class));
}
private static MethodHandle MH_mybsm() throws ReflectiveOperationException {
verboseUntransformed(null);
return lookup().findStatic(Example.class, "mybsm", MT_gen3());
}
private static MethodHandle INDY_tester;
private static MethodHandle INDY_tester() throws Throwable {
if (INDY_tester != null) return INDY_tester;
CallSite cs = (CallSite) MH_mybsm().invokeWithArguments(lookup(), "myindyname",
fromMethodDescriptorString("(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;", null));
verboseUntransformed("BSM produces CS: "+cs);
return cs.dynamicInvoker();
}
}
3.从网站上下载的ExmapleTest需要稍作修改,修改后的内容如下;
package indify;
import org.junit.Test;
/**
*
* @author John Rose <john.rose at sun.com>
*/
public class ExampleTest {
@Test
public void testMain() throws Throwable {
System.out.println("main");
Indify.main("--verify-specifier-count=1",
/*INDY工具将Example.class转换成等效的class文件存放路径*/
"--dest=E:/",
"--verbose",
"--expand-properties", "--classpath", "${java.class.path}",
/*输入class文件Example.class的全路径,然后使用javap打开该文件*/
"E:\\workspaces\\JVMDemo\\bin\\indify\\Example.class");
//Example.main();
}
}
4.使用junit进行单元测试,输出内容如下:
下一篇: 华为的MUX VLAN功能及配置