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

[Bug]: Unable to parse CtElement from a JDK-runtime-element by CtPath #4984

Open
kaml8 opened this issue Oct 27, 2022 · 4 comments · May be fixed by #4989
Open

[Bug]: Unable to parse CtElement from a JDK-runtime-element by CtPath #4984

kaml8 opened this issue Oct 27, 2022 · 4 comments · May be fixed by #4989
Labels

Comments

@kaml8
Copy link

kaml8 commented Oct 27, 2022

Describe the bug

Ideally, there's a one-to-one relationship between a CtElement and a specific complete CtPath.

It works well with user-defined classes, but not with a JDK-runtime element.

For example, I can't get CtType for java.lang.System from its CtPath:
#subPackage[name=java]#subPackage[name=lang]#containedType[name=System]

Source code you are trying to analyze/transform

java.lang.System

Source code for your Spoon processing

@Test
    public void test() {
        CtType<?> ctType = new TypeFactory().get(java.lang.System.class);
        System.out.println(ctType.getPath());

        CtPath path = new CtPathStringBuilder().fromString("#subPackage[name=java]#subPackage[name=lang]#containedType[name=System]");
        List<CtElement> l = path.evaluateOn(model.getRootPackage());
        System.out.println(l);

        assertTrue(l.iterator().hasNext());
        assertEquals(l.iterator().next(), ctType);
    }

Actual output

#subPackage[name=java]#subPackage[name=lang]#containedType[name=System]
[]

java.lang.AssertionError
	at org.junit.Assert.fail(Assert.java:86)
	at org.junit.Assert.assertTrue(Assert.java:41)
	at org.junit.Assert.assertTrue(Assert.java:52)

Expected output

#subPackage[name=java]#subPackage[name=lang]#containedType[name=System]
[public final class System {
    private System() {
    }

    private static void initPhase1() {
    }

    private static int initPhase2(boolean arg0, boolean arg1) {
    }

    private static void initPhase3() {
    }

    public static void exit(int arg0) {
    }

    public static void runFinalization() {
    }

    public static java.lang.String getProperty(java.lang.String arg0) {
    }

    public static java.lang.String getProperty(java.lang.String arg0, java.lang.String arg1) {
    }

    @jdk.internal.HotSpotIntrinsicCandidate
    public static native int identityHashCode(java.lang.Object arg0) {
    }

    @jdk.internal.HotSpotIntrinsicCandidate
    public static native long currentTimeMillis() {
    }

    @jdk.internal.HotSpotIntrinsicCandidate
    public static native long nanoTime() {
    }

    @jdk.internal.HotSpotIntrinsicCandidate
    public static native void arraycopy(java.lang.Object arg0, int arg1, java.lang.Object arg2, int arg3, int arg4) {
    }

    private static native void registerNatives() {
    }

    public static java.lang.SecurityManager getSecurityManager() {
    }

    @jdk.internal.reflect.CallerSensitive
    public static void loadLibrary(java.lang.String arg0) {
    }

    public static native java.lang.String mapLibraryName(java.lang.String arg0) {
    }

    public static java.lang.String lineSeparator() {
    }

    public static void setIn(java.io.InputStream arg0) {
    }

    public static void setOut(java.io.PrintStream arg0) {
    }

    public static void setErr(java.io.PrintStream arg0) {
    }

    public static java.io.Console console() {
    }

    public static java.nio.channels.Channel inheritedChannel() throws java.io.IOException {
    }

    private static void checkIO() {
    }

    private static native void setIn0(java.io.InputStream arg0) {
    }

    private static native void setOut0(java.io.PrintStream arg0) {
    }

    private static native void setErr0(java.io.PrintStream arg0) {
    }

    public static void setSecurityManager(java.lang.SecurityManager arg0) {
    }

    private static synchronized void setSecurityManager0(java.lang.SecurityManager arg0) {
    }

    private static native java.util.Properties initProperties(java.util.Properties arg0) {
    }

    public static java.util.Properties getProperties() {
    }

    public static void setProperties(java.util.Properties arg0) {
    }

    public static java.lang.String setProperty(java.lang.String arg0, java.lang.String arg1) {
    }

    public static java.lang.String clearProperty(java.lang.String arg0) {
    }

    private static void checkKey(java.lang.String arg0) {
    }

    public static java.lang.String getenv(java.lang.String arg0) {
    }

    public static java.util.Map<java.lang.String, java.lang.String> getenv() {
    }

    @jdk.internal.reflect.CallerSensitive
    public static java.lang.System.Logger getLogger(java.lang.String arg0, java.util.ResourceBundle arg1) {
    }

    @jdk.internal.reflect.CallerSensitive
    public static java.lang.System.Logger getLogger(java.lang.String arg0) {
    }

    public static void gc() {
    }

    @jdk.internal.reflect.CallerSensitive
    public static void load(java.lang.String arg0) {
    }

    private static java.io.PrintStream newPrintStream(java.io.FileOutputStream arg0, java.lang.String arg1) {
    }

    private static void logInitException(boolean arg0, boolean arg1, java.lang.String arg2, java.lang.Throwable arg3) {
    }

    private static void setJavaLangAccess() {
    }

    public static final java.io.InputStream in;

    public static final java.io.PrintStream out;

    public static final java.io.PrintStream err;

    private static volatile java.io.Console cons;

    private static java.util.Properties props;

    private static java.lang.String lineSeparator;

    static java.lang.ModuleLayer bootLayer;

    public static abstract class LoggerFinder {
        protected LoggerFinder() {
        }

        private LoggerFinder(java.lang.Void arg0) {
        }

        private static java.lang.Void checkPermission() {
        }

        public abstract java.lang.System.Logger getLogger(java.lang.String arg0, java.lang.Module arg1);

        static java.lang.System.LoggerFinder accessProvider() {
        }

        public java.lang.System.Logger getLocalizedLogger(java.lang.String arg0, java.util.ResourceBundle arg1, java.lang.Module arg2) {
        }

        public static java.lang.System.LoggerFinder getLoggerFinder() {
        }

        static final java.lang.RuntimePermission LOGGERFINDER_PERMISSION;

        private static volatile java.lang.System.LoggerFinder service;
    }

    public static abstract interface Logger {
        public abstract java.lang.String getName();

        public default void log(java.lang.System.Logger.Level arg0, java.lang.String arg1, java.lang.Object... arg2) {
        }

        public default void log(java.lang.System.Logger.Level arg0, java.util.function.Supplier<java.lang.String> arg1, java.lang.Throwable arg2) {
        }

        public default void log(java.lang.System.Logger.Level arg0, java.lang.String arg1, java.lang.Throwable arg2) {
        }

        public abstract void log(java.lang.System.Logger.Level arg0, java.util.ResourceBundle arg1, java.lang.String arg2, java.lang.Throwable arg3);

        public abstract void log(java.lang.System.Logger.Level arg0, java.util.ResourceBundle arg1, java.lang.String arg2, java.lang.Object... arg3);

        public default void log(java.lang.System.Logger.Level arg0, java.lang.String arg1) {
        }

        public default void log(java.lang.System.Logger.Level arg0, java.util.function.Supplier<java.lang.String> arg1) {
        }

        public default void log(java.lang.System.Logger.Level arg0, java.lang.Object arg1) {
        }

        public abstract boolean isLoggable(java.lang.System.Logger.Level arg0);

        public static final enum Level {

            ALL,
            TRACE,
            DEBUG,
            INFO,
            WARNING,
            ERROR,
            OFF;
            private Level(int arg0) {
            }

            public final java.lang.String getName() {
            }

            public final int getSeverity() {
            }

            private final int severity;
        }
    }
}]

Spoon Version

10.2.0-beta-21

JVM Version

11

What operating system are you using?

Windows 11

@kaml8 kaml8 added the bug label Oct 27, 2022
@MartinWitt
Copy link
Collaborator

JDK classes are generated on the fly as needed by reflection. CtPathElements only exist for elements of the input model. So, it seems like this result is intended, and you can't use CtPath for JDK types.
I'm unsure if we can integrate shadow elements in the CtPath lookup.

@kaml8
Copy link
Author

kaml8 commented Oct 28, 2022

Thanks for replying.

I am trying to analysis invocations in executables and describe them in a graph database,
which includes a lot of JDK callings: fields, variables, methods and so on.

I choose CtPath as a primary key( as String ) so that I can switch between graph nodes and spoon element as needed.

Of course I can code a function that transform from CtPath to JDK CtElement manually, but maybe it's a good idea to add this as an API optionally?

A JDK CtElement owns its CtPath, it is also reasonable for a CtPath( as String ) to get its CtElement, even it is a shadow type, even the process logic may differ from that in parsing input model.

How do you think about it?

@kaml8
Copy link
Author

kaml8 commented Oct 28, 2022

Besides, I noticed that ctPath.evaluateOn() can run successfully without given a root because of the changable parameter list CtElement... startNode, maybe the empty parameter case can be reused for JDK types?

@MartinWitt
Copy link
Collaborator

Of course I can code a function that transform from CtPath to JDK CtElement manually, but maybe it's a good idea to add this as an API optionally?

After some additional consideration, I concluded that the user would benefit from this. The implementation of CtPath is way before I worked on spoon, and I never used it. So, I cannot really tell how much work it is to implement this feature.

Besides, I noticed that ctPath.evaluateOn() can run successfully without given a root because of the changable parameter list CtElement... startNode, maybe the empty parameter case can be reused for JDK types?

To my understanding the empty list simply uses the root element for the search so yes this could be reused for CtPath lookups for jdk elements.

Do you want to give the implementation of the feature a try?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
2 participants