Skip to content
Closed
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 @@ -51,6 +51,8 @@
public final class LambdaUtils {
private static final Pattern LAMBDA_PATTERN = Pattern.compile("\\$\\$Lambda\\$\\d+[/\\.][^/]+;");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use this one for all we need?

Copy link
Contributor Author

@filozof50 filozof50 Sep 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the explanation is in the comment above.

private static final char[] HEX = "0123456789abcdef".toCharArray();
public static final String LAMBDA_SPLIT_PATTERN = "\\$\\$Lambda\\$";
public static final String LAMBDA_CLASS_NAME_SUBSTRING = "$$Lambda$";

private static GraphBuilderConfiguration buildLambdaParserConfig(ClassInitializationPlugin cip) {
GraphBuilderConfiguration.Plugins plugins = new GraphBuilderConfiguration.Plugins(new InvocationPlugins());
Expand Down Expand Up @@ -107,7 +109,7 @@ public static String findStableLambdaName(ClassInitializationPlugin cip, Provide

public static boolean isLambdaType(ResolvedJavaType type) {
String typeName = type.getName();
return type.isFinalFlagSet() && typeName.contains("/") && typeName.contains("$$Lambda$") && lambdaMatcher(type.getName()).find();
return type.isFinalFlagSet() && typeName.contains("/") && typeName.contains(LAMBDA_CLASS_NAME_SUBSTRING) && lambdaMatcher(type.getName()).find();
}

private static String createStableLambdaName(ResolvedJavaType lambdaType, List<ResolvedJavaMethod> targetMethods) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ public interface RuntimeSerializationSupport {

void registerWithTargetConstructorClass(ConfigurationCondition condition, String className, String customTargetConstructorClassName);

void registerLambdaCapturingClass(ConfigurationCondition condition, String lambdaCapturingClassName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import org.graalvm.compiler.java.LambdaUtils;

public final class CallTreePrinter {

Expand Down Expand Up @@ -311,7 +312,7 @@ public Set<String> classesSet(boolean packageNameOnly) {
String name = method.getDeclaringClass().toJavaName(true);
if (packageNameOnly) {
name = packagePrefix(name);
if (name.contains("$$Lambda$")) {
if (name.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) {
/* Also strip synthetic package names added for lambdas. */
name = packagePrefix(name);
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet {
private JNIFieldId javaUtilResourceBundleParentField;
private JNIMethodId javaUtilResourceBundleGetLocale;

final JNIFieldId javaLangInvokeSerializedLambdaCapturingClass;

NativeImageAgentJNIHandleSet(JNIEnvironment env) {
super(env);
javaLangClass = newClassGlobalRef(env, "java/lang/Class");
Expand Down Expand Up @@ -113,6 +115,9 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet {
javaLangIllegalAccessError = newClassGlobalRef(env, "java/lang/IllegalAccessError");
javaLangInvokeWrongMethodTypeException = newClassGlobalRef(env, "java/lang/invoke/WrongMethodTypeException");
javaLangIllegalArgumentException = newClassGlobalRef(env, "java/lang/IllegalArgumentException");

JNIObjectHandle serializedLambda = findClass(env, "java/lang/invoke/SerializedLambda");
javaLangInvokeSerializedLambdaCapturingClass = getFieldId(env, serializedLambda, "capturingClass", "Ljava/lang/Class;", false);
}

JNIMethodId getJavaLangReflectExecutableGetParameterTypes(JNIEnvironment env) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.oracle.svm.configure.json.JsonPrintable;
import org.graalvm.compiler.java.LambdaUtils;
import org.graalvm.nativeimage.impl.ConfigurationCondition;
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;

Expand All @@ -40,36 +42,60 @@

public class SerializationConfiguration implements ConfigurationBase, RuntimeSerializationSupport {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you're using subclassing so you can store two different kinds of types together here that would really better be separated in two maps: captured types and capturing types. The two should also be separated in the configuration file. Then two classes would also be sufficient: a type and a captured type with a custom constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separated as suggested.


private final Set<SerializationConfigurationType> serializations = ConcurrentHashMap.newKeySet();
private final Set<SerializationConfigurationType> serializationTypes = ConcurrentHashMap.newKeySet();
private final Set<SerializationConfigurationLambdaCapturingType> lambdaSerializationCapturingTypes = ConcurrentHashMap.newKeySet();

public SerializationConfiguration() {
}

public SerializationConfiguration(SerializationConfiguration other) {
serializations.addAll(other.serializations);
serializationTypes.addAll(other.serializationTypes);
lambdaSerializationCapturingTypes.addAll(other.lambdaSerializationCapturingTypes);
}

public void removeAll(SerializationConfiguration other) {
serializations.removeAll(other.serializations);
serializationTypes.removeAll(other.serializationTypes);
lambdaSerializationCapturingTypes.removeAll(other.lambdaSerializationCapturingTypes);
}

public boolean contains(ConfigurationCondition condition, String serializationTargetClass, String customTargetConstructorClass) {
return serializations.contains(createConfigurationType(condition, serializationTargetClass, customTargetConstructorClass));
return serializationTypes.contains(createConfigurationType(condition, serializationTargetClass, customTargetConstructorClass)) ||
lambdaSerializationCapturingTypes.contains(createLambdaCapturingClassConfigurationType(condition, serializationTargetClass));
}

@Override
public void printJson(JsonWriter writer) throws IOException {
writer.append('[').indent();
writer.append('{').indent().newline();
List<SerializationConfigurationType> listOfCapturedClasses = new ArrayList<>(serializationTypes);
Collections.sort(listOfCapturedClasses);
printSerializationClasses(writer, "types", listOfCapturedClasses);
writer.append(",").newline();
List<SerializationConfigurationLambdaCapturingType> listOfCapturingClasses = new ArrayList<>(lambdaSerializationCapturingTypes);
listOfCapturingClasses.sort(new SerializationConfigurationLambdaCapturingType.SerializationConfigurationLambdaCapturingTypesComparator());
printSerializationClasses(writer, "lambdaCapturingTypes", listOfCapturingClasses);
writer.unindent().newline();
writer.append('}');
}

private static void printSerializationClasses(JsonWriter writer, String types, List<? extends JsonPrintable> serializationConfigurationTypes) throws IOException {
writer.quote(types).append(":");
writer.append('[');
writer.indent();

printSerializationTypes(serializationConfigurationTypes, writer);

writer.unindent().newline();
writer.append("]");
}

private static void printSerializationTypes(List<? extends JsonPrintable> serializationConfigurationTypes, JsonWriter writer) throws IOException {
String prefix = "";
List<SerializationConfigurationType> list = new ArrayList<>(serializations);
Collections.sort(list);
for (SerializationConfigurationType type : list) {

for (JsonPrintable type : serializationConfigurationTypes) {
writer.append(prefix).newline();
type.printJson(writer);
prefix = ",";
}
writer.unindent().newline();
writer.append(']');
}

@Override
Expand All @@ -91,17 +117,27 @@ public void registerWithTargetConstructorClass(ConfigurationCondition condition,

@Override
public void registerWithTargetConstructorClass(ConfigurationCondition condition, String className, String customTargetConstructorClassName) {
serializations.add(createConfigurationType(condition, className, customTargetConstructorClassName));
serializationTypes.add(createConfigurationType(condition, className, customTargetConstructorClassName));
}

@Override
public void registerLambdaCapturingClass(ConfigurationCondition condition, String lambdaCapturingClassName) {
lambdaSerializationCapturingTypes.add(createLambdaCapturingClassConfigurationType(condition, lambdaCapturingClassName.split(LambdaUtils.LAMBDA_SPLIT_PATTERN)[0]));
}

@Override
public boolean isEmpty() {
return serializations.isEmpty();
return serializationTypes.isEmpty() && lambdaSerializationCapturingTypes.isEmpty();
}

private static SerializationConfigurationType createConfigurationType(ConfigurationCondition condition, String className, String customTargetConstructorClassName) {
String convertedClassName = SignatureUtil.toInternalClassName(className);
String convertedCustomTargetConstructorClassName = customTargetConstructorClassName == null ? null : SignatureUtil.toInternalClassName(customTargetConstructorClassName);
return new SerializationConfigurationType(condition, convertedClassName, convertedCustomTargetConstructorClassName);
}

private static SerializationConfigurationLambdaCapturingType createLambdaCapturingClassConfigurationType(ConfigurationCondition condition, String className) {
String convertedClassName = SignatureUtil.toInternalClassName(className);
return new SerializationConfigurationLambdaCapturingType(condition, convertedClassName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2021, 2021, 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 com.oracle.svm.configure.config;

import java.io.IOException;
import java.util.Comparator;
import java.util.Objects;

import com.oracle.svm.configure.json.JsonPrintable;
import org.graalvm.nativeimage.impl.ConfigurationCondition;

import com.oracle.svm.configure.json.JsonWriter;
import com.oracle.svm.core.configure.SerializationConfigurationParser;

public class SerializationConfigurationLambdaCapturingType implements JsonPrintable {
private final ConfigurationCondition condition;
private final String qualifiedJavaName;

public SerializationConfigurationLambdaCapturingType(ConfigurationCondition condition, String qualifiedJavaName) {
assert qualifiedJavaName.indexOf('/') == -1 : "Requires qualified Java name, not the internal representation";
Objects.requireNonNull(condition);
this.condition = condition;
Objects.requireNonNull(qualifiedJavaName);
this.qualifiedJavaName = qualifiedJavaName;
}

@Override
public void printJson(JsonWriter writer) throws IOException {
writer.append('{').indent().newline();
ConfigurationConditionPrintable.printConditionAttribute(condition, writer);

writer.quote(SerializationConfigurationParser.NAME_KEY).append(":").quote(qualifiedJavaName);
writer.unindent().newline().append('}');
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SerializationConfigurationLambdaCapturingType that = (SerializationConfigurationLambdaCapturingType) o;
return condition.equals(that.condition) &&
qualifiedJavaName.equals(that.qualifiedJavaName);
}

@Override
public int hashCode() {
return Objects.hash(condition, qualifiedJavaName);
}

public static final class SerializationConfigurationLambdaCapturingTypesComparator implements Comparator<SerializationConfigurationLambdaCapturingType> {

@Override
public int compare(SerializationConfigurationLambdaCapturingType o1, SerializationConfigurationLambdaCapturingType o2) {
int compareName = o1.qualifiedJavaName.compareTo(o2.qualifiedJavaName);
if (compareName != 0) {
return compareName;
}
return o1.condition.compareTo(o2.condition);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ public class SerializationConfigurationType implements JsonPrintable, Comparable
private final String qualifiedCustomTargetConstructorJavaName;

public SerializationConfigurationType(ConfigurationCondition condition, String qualifiedJavaName, String qualifiedCustomTargetConstructorJavaName) {
assert qualifiedJavaName.indexOf('/') == -1 : "Requires qualified Java name, not internal representation";
assert qualifiedJavaName.indexOf('/') == -1 : "Requires qualified Java name, not the internal representation";
assert !qualifiedJavaName.startsWith("[") : "Requires Java source array syntax, for example java.lang.String[]";
assert qualifiedCustomTargetConstructorJavaName == null || qualifiedCustomTargetConstructorJavaName.indexOf('/') == -1 : "Requires qualified Java name, not internal representation";
assert qualifiedCustomTargetConstructorJavaName == null || qualifiedCustomTargetConstructorJavaName.indexOf('/') == -1 : "Requires qualified Java name, not the internal representation";
assert qualifiedCustomTargetConstructorJavaName == null || !qualifiedCustomTargetConstructorJavaName.startsWith("[") : "Requires Java source array syntax, for example java.lang.String[]";
Objects.requireNonNull(condition);
this.condition = condition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.List;
import java.util.Map;

import org.graalvm.compiler.java.LambdaUtils;
import org.graalvm.nativeimage.impl.ConfigurationCondition;

import com.oracle.svm.configure.config.SerializationConfiguration;
Expand All @@ -54,14 +55,29 @@ void processEntry(Map<String, ?> entry) {
}
String function = (String) entry.get("function");
List<?> args = (List<?>) entry.get("args");

if ("ObjectStreamClass.<init>".equals(function)) {
expectSize(args, 2);

if (advisor.shouldIgnore(LazyValueUtils.lazyValue((String) args.get(0)), LazyValueUtils.lazyValue(null))) {
return;
}

serializationConfiguration.registerWithTargetConstructorClass(condition, (String) args.get(0), (String) args.get(1));
String className = (String) args.get(0);

if (className.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) {
serializationConfiguration.registerLambdaCapturingClass(condition, className);
} else {
serializationConfiguration.registerWithTargetConstructorClass(condition, className, (String) args.get(1));
}
} else if ("SerializedLambda.readResolve".equals(function)) {
expectSize(args, 1);

if (advisor.shouldIgnore(LazyValueUtils.lazyValue((String) args.get(0)), LazyValueUtils.lazyValue(null))) {
return;
}

serializationConfiguration.registerLambdaCapturingClass(condition, (String) args.get(0));
}
}
}
Loading