Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

* transforms unknown dynamic invoke into NoSuchMethodError #764

Merged
merged 2 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,7 @@
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.clazz.Clazzes;
import org.robovm.compiler.clazz.Path;
import org.robovm.compiler.config.ConfigXmlEntries.AppExtensionsList;
import org.robovm.compiler.config.ConfigXmlEntries.ClasspathentryList;
import org.robovm.compiler.config.ConfigXmlEntries.ForceLinkClassesList;
import org.robovm.compiler.config.ConfigXmlEntries.ForceLinkMethodsList;
import org.robovm.compiler.config.ConfigXmlEntries.FrameworksList;
import org.robovm.compiler.config.ConfigXmlEntries.LibsList;
import org.robovm.compiler.config.ConfigXmlEntries.PathsList;
import org.robovm.compiler.config.ConfigXmlEntries.PluginArgumentsList;
import org.robovm.compiler.config.ConfigXmlEntries.ResourcesList;
import org.robovm.compiler.config.ConfigXmlEntries.RootsList;
import org.robovm.compiler.config.ConfigXmlEntries.SymbolsList;
import org.robovm.compiler.config.ConfigXmlEntries.*;
import org.robovm.compiler.config.StripArchivesConfig.StripArchivesBuilder;
import org.robovm.compiler.config.tools.Tools;
import org.robovm.compiler.llvm.DataLayout;
Expand All @@ -43,8 +33,7 @@
import org.robovm.compiler.plugin.annotation.AnnotationImplPlugin;
import org.robovm.compiler.plugin.debug.DebugInformationPlugin;
import org.robovm.compiler.plugin.debug.DebuggerLaunchPlugin;
import org.robovm.compiler.plugin.desugar.StringConcatRewriterPlugin;
import org.robovm.compiler.plugin.lambda.LambdaPlugin;
import org.robovm.compiler.plugin.invokedynamic.InvokeDynamicCompilerPlugin;
import org.robovm.compiler.plugin.objc.*;
import org.robovm.compiler.target.ConsoleTarget;
import org.robovm.compiler.target.Target;
Expand Down Expand Up @@ -245,8 +234,7 @@ protected Config(UUID uuid) {
new ObjCMemberPlugin(),
new ObjCBlockPlugin(),
new AnnotationImplPlugin(),
new StringConcatRewriterPlugin(),
new LambdaPlugin(),
new InvokeDynamicCompilerPlugin(),
new DebugInformationPlugin(),
new DebuggerLaunchPlugin(),
new BuildGarbageCollectorPlugin()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package org.robovm.compiler.plugin.invokedynamic;

import org.robovm.compiler.CompilerException;
import org.robovm.compiler.ModuleBuilder;
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.plugin.AbstractCompilerPlugin;
import org.robovm.compiler.plugin.invokedynamic.lambda.LambdaPlugin;
import org.robovm.compiler.plugin.invokedynamic.stringconcat.StringConcatRewriterPlugin;
import soot.*;
import soot.jimple.*;

import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class InvokeDynamicCompilerPlugin extends AbstractCompilerPlugin {

/**
* Delegate for specific bootstrap method handler
* (specific implementation to be done there)
*/
public interface Delegate {
default void beforeClass(Config config, Clazz clazz, ModuleBuilder moduleBuilder) throws IOException {
}

default void beforeMethod(Config config, Clazz clazz, SootMethod method, ModuleBuilder moduleBuilder) throws IOException {
}

default void afterClass(Config config, Clazz clazz, ModuleBuilder moduleBuilder) throws IOException {
}

LinkedList<Unit> transformDynamicInvoke(
Config config, Clazz clazz, SootClass sootClass, SootMethod method, DefinitionStmt defStmt,
DynamicInvokeExpr invokeExpr, ModuleBuilder moduleBuilder) throws IOException;
}

private final List<Delegate> supportedDynamicInvokes;

public InvokeDynamicCompilerPlugin() {
supportedDynamicInvokes = List.of(
new LambdaPlugin(),
new StringConcatRewriterPlugin(),
new UnrecognizedBootstrapDelegate() // has to be declared last !
);
}

@Override
public void beforeClass(Config config, Clazz clazz, ModuleBuilder moduleBuilder) throws IOException {
SootClass sootClass = clazz.getSootClass();

// deliver beforeClass notification to allow delegates to initializes
for (Delegate delegate : supportedDynamicInvokes)
delegate.beforeClass(config, clazz, moduleBuilder);

for (SootMethod method : sootClass.getMethods()) {
// deliver beforeMethod notification to allow delegates to reset counters whatever
for (Delegate delegate : supportedDynamicInvokes)
delegate.beforeMethod(config, clazz, method, moduleBuilder);

transformMethod(config, clazz, sootClass, method, moduleBuilder);
}

// deliver afterClass notification to allow delegates to reset class related activities
for (Delegate delegate : supportedDynamicInvokes)
delegate.afterClass(config, clazz, moduleBuilder);
}

private void transformMethod(Config config, Clazz clazz, SootClass sootClass,
SootMethod method, ModuleBuilder moduleBuilder) throws IOException {
if (!method.isConcrete())
return;

Body body = method.retrieveActiveBody();
PatchingChain<Unit> units = body.getUnits();
for (Unit unit = units.getFirst(); unit != null; unit = body.getUnits().getSuccOf(unit)) {
if (unit instanceof DefinitionStmt) {
DefinitionStmt defStmt = (DefinitionStmt) unit;
if (defStmt.getRightOp() instanceof DynamicInvokeExpr) {
DynamicInvokeExpr invokeExpr = (DynamicInvokeExpr) ((DefinitionStmt) unit).getRightOp();
LinkedList<Unit> newUnits = null;
try {
for (Delegate delegate : supportedDynamicInvokes) {
newUnits = delegate.transformDynamicInvoke(config, clazz, sootClass, method, defStmt,
invokeExpr, moduleBuilder);
if (newUnits != null)
break;
}

// should not happen as there is fallback
assert newUnits != null;

units.insertAfter(newUnits, unit);
units.remove(unit);
unit = newUnits.getLast();

} catch (Throwable e) {
// TODO: Change the jimple of the method to throw a
// LambdaConversionException at runtime.
throw new CompilerException(e);
}
}
}
}
}

/**
* Fallback that will insert NoSuchMethodError exception for any attempt to invoke
*/
private static class UnrecognizedBootstrapDelegate implements Delegate {
private int tmpCounter = 0;

private boolean initialized = false;
private SootClass java_lang_NoSuchMethodError;
private SootMethodRef java_lang_NoSuchMethodError_init;

/**
* performs lazy initializations.
* can't perform it in constructor as config and plugins are being created before soot is
* initialized and it will perform G.reset() which causes VoidType.v() to be not equals as received
* from different globals. It seems to be a wide project bug in Soot's equal implementations.
* Check VoidType.equals for example
*/
private void initializeIfRequired() {
if (initialized)
return;

SootResolver r = SootResolver.v();
SootClass java_lang_String = r.makeClassRef("java.lang.String");

java_lang_NoSuchMethodError = r.makeClassRef("java.lang.NoSuchMethodError");
java_lang_NoSuchMethodError_init =
Scene.v().makeMethodRef(
java_lang_NoSuchMethodError,
"<init>",
Collections.singletonList(java_lang_String.getType()),
VoidType.v(), false);
initialized = true;
}

@Override
public void beforeMethod(Config config, Clazz clazz, SootMethod method, ModuleBuilder moduleBuilder) {
tmpCounter = 0;
}

@Override
public LinkedList<Unit> transformDynamicInvoke(
Config config, Clazz clazz, SootClass sootClass, SootMethod method,
DefinitionStmt defStmt, DynamicInvokeExpr invokeExpr,
ModuleBuilder moduleBuilder)
{
initializeIfRequired();
String msg = "Unsupported InvokeDynamic to " + invokeExpr.getBootstrapMethodRef().declaringClass().getName() +
'.' + invokeExpr.getBootstrapMethodRef().name();
Jimple jimple = Jimple.v();
Body body = method.retrieveActiveBody();
LinkedList<Unit> newUnits = new LinkedList<>();

// initialize left size of expression just to keep a variable in scope
Type localType = defStmt.getLeftOp().getType();
Value dummyConstant;
if (localType instanceof RefLikeType)
dummyConstant = NullConstant.v();
else if (localType instanceof IntegerType)
dummyConstant = IntConstant.v(0);
else if (localType instanceof LongType)
dummyConstant = LongConstant.v(0);
else if (localType instanceof FloatType)
dummyConstant = FloatConstant.v(0);
else if (localType instanceof DoubleType)
dummyConstant = DoubleConstant.v(0);
else throw new IllegalStateException("Unexpected local type " + localType);
newUnits.add(jimple.newAssignStmt(defStmt.getLeftOp(), dummyConstant));

// insert exception
Local exc = jimple.newLocal("$tmp_invdyn_exc" + (tmpCounter++), java_lang_NoSuchMethodError.getType());
body.getLocals().add(exc);
newUnits.add(jimple.newAssignStmt(exc, jimple.newNewExpr(java_lang_NoSuchMethodError.getType())));
newUnits.add(jimple.newInvokeStmt(jimple.newSpecialInvokeExpr(exc, java_lang_NoSuchMethodError_init,
StringConstant.v(msg))));
newUnits.add(jimple.newThrowStmt(exc));

return newUnits;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
*/
package org.robovm.compiler.plugin.lambda;
package org.robovm.compiler.plugin.invokedynamic.lambda;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
*/
package org.robovm.compiler.plugin.lambda;
package org.robovm.compiler.plugin.invokedynamic.lambda;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
Expand Down
Loading
Loading