java-topology/test/hotspot/jtreg/compiler/calls/common/InvokeDynamicPatcher.java
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

143 lines
7.6 KiB
Java

/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package compiler.calls.common;
import java.io.IOException;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeElement;
import java.lang.classfile.CodeTransform;
import java.lang.classfile.Label;
import java.lang.constant.ClassDesc;
import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.DynamicCallSiteDesc;
import java.lang.constant.MethodHandleDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import static java.lang.constant.ConstantDescs.*;
/**
* A class which patch InvokeDynamic class bytecode with invokydynamic
instruction, rewriting "caller" method to call "callee" method using
invokedynamic
*/
public final class InvokeDynamicPatcher {
private static final ClassDesc CLASS = InvokeDynamic.class.describeConstable().orElseThrow();
private static final String CALLER_METHOD_NAME = "caller";
private static final String CALLEE_METHOD_NAME = "callee";
private static final String NATIVE_CALLEE_METHOD_NAME = "calleeNative";
private static final String BOOTSTRAP_METHOD_NAME = "bootstrapMethod";
private static final String CALL_NATIVE_FIELD = "nativeCallee";
private static final ClassDesc CALL_NATIVE_FIELD_DESC = CD_boolean;
private static final MethodTypeDesc CALLEE_METHOD_DESC = MethodTypeDesc.of(
CD_boolean, CLASS, CD_int, CD_long, CD_float, CD_double, CD_String);
private static final MethodTypeDesc ASSERTTRUE_METHOD_DESC = MethodTypeDesc.of(
CD_void, CD_boolean, CD_String);
private static final ClassDesc ASSERTS_CLASS = ClassDesc.ofInternalName("jdk/test/lib/Asserts");
private static final String ASSERTTRUE_METHOD_NAME = "assertTrue";
public static void main(String args[]) throws IOException, URISyntaxException {
Path filePath = Path.of(InvokeDynamic.class.getProtectionDomain().getCodeSource()
.getLocation().toURI()).resolve(InvokeDynamic.class.getName().replace('.', '/') +".class");
var bytes = ClassFile.of().transformClass(ClassFile.of().parse(filePath),
ClassTransform.transformingMethodBodies(m -> m.methodName().equalsString(CALLER_METHOD_NAME), new CodeTransform() {
@Override
public void accept(CodeBuilder builder, CodeElement element) {
// discard
}
/* the code generated looks like
* 0: aload_0
* 1: ldc #125 // int 1
* 3: ldc2_w #126 // long 2l
* 6: ldc #128 // float 3.0f
* 8: ldc2_w #129 // double 4.0d
* 11: ldc #132 // String 5
* 13: aload_0
* 14: getfield #135 // Field nativeCallee:Z
* 17: ifeq 28
* 20: invokedynamic #181, 0 // InvokeDynamic #1:calleeNative:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z
* 25: goto 33
* 28: invokedynamic #183, 0 // InvokeDynamic #1:callee:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z
* 33: ldc #185 // String Call insuccessfull
* 35: invokestatic #191 // Method jdk/test/lib/Asserts.assertTrue:(ZLjava/lang/String;)V
* 38: return
*
* or, using java-like pseudo-code
* if (this.nativeCallee == false) {
* invokedynamic-call-return-value = invokedynamic-of-callee
* } else {
* invokedynamic-call-return-value = invokedynamic-of-nativeCallee
* }
* Asserts.assertTrue(invokedynamic-call-return-value, error-message);
* return;
*/
@Override
public void atEnd(CodeBuilder builder) {
Label nonNativeLabel = builder.newLabel();
Label checkLabel = builder.newLabel();
MethodType mtype = MethodType.methodType(CallSite.class,
MethodHandles.Lookup.class, String.class, MethodType.class);
DirectMethodHandleDesc dmh = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC,
CLASS, BOOTSTRAP_METHOD_NAME, mtype.descriptorString());
// push callee parameters onto stack
builder.aload(builder.receiverSlot())
.ldc(1)
.ldc(2L)
.ldc(3.0f)
.ldc(4.0d)
.ldc("5")
// params loaded. let's decide what method to call
.aload(builder.receiverSlot())
// get nativeCallee field
.getfield(CLASS, CALL_NATIVE_FIELD, CALL_NATIVE_FIELD_DESC)
// if nativeCallee == false goto nonNativeLabel
.ifeq(nonNativeLabel)
// invokedynamic nativeCalleeMethod using bootstrap method
.invokedynamic(DynamicCallSiteDesc.of(dmh, NATIVE_CALLEE_METHOD_NAME, CALLEE_METHOD_DESC))
// goto checkLabel
.goto_(checkLabel)
// label: nonNativeLabel
.labelBinding(nonNativeLabel)
// invokedynamic calleeMethod using bootstrap method
.invokedynamic(DynamicCallSiteDesc.of(dmh, CALLEE_METHOD_NAME, CALLEE_METHOD_DESC))
.labelBinding(checkLabel)
.ldc(CallsBase.CALL_ERR_MSG)
.invokestatic(ASSERTS_CLASS, ASSERTTRUE_METHOD_NAME, ASSERTTRUE_METHOD_DESC)
// label: return
.return_();
}
}));
Files.write(filePath, bytes, StandardOpenOption.WRITE);
}
}