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

Example for declare(FieldId<?, ?> fieldId, int flags, Object staticValue) #110

Open
hamada147 opened this issue Jul 2, 2018 · 9 comments

Comments

@hamada147
Copy link

hamada147 commented Jul 2, 2018

I would like to create a norma modal with all its fields are public but I can't figure our how to use the provided function declare(FieldId fieldId, int flags, Object staticValue). Can someone please provide me with a working example

I want to create something like that

public class ModelA {
    public String value1;
    public String value2;
    public String value3;
}

My issue is with FieldId value for the declare function.

this.dexMaker.declare(???, Modifier.PUBLIC, "");
@hamada147
Copy link
Author

hamada147 commented Jul 3, 2018

I tried to my best knowledge and here is what I managed to make so far
The following is the main package com.kn.testcreatingaclassinruntime
CreateClassInRunTimeWrapper is another package inside of it

package com.kn.testcreatingaclassinruntime.CreateClassInRunTimeWrapper;

import com.android.dx.DexMaker;
import com.android.dx.FieldId;
import com.android.dx.TypeId;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;

/**
 * This is a DexMaker wrapper to create classes in run time
 * because the existing code is way too big and confusing for me
 * @author Ahmed Moussa <email>[email protected]</email>
 * @version 1.0
 * Created by Ahmed Moussa on 7/2/18.
 */
public final class DexMakerWrapper {

    // ClassName
    private String ClassName;
    // dexMaker used to create the class
    private DexMaker dexMaker;
    // class declaration after finishing
    private Class<?> createdClass;

    // make and entry point to the train methodology
    public static DexMakerWrapper Constructor() {
        return new DexMakerWrapper();
    }
    
    // use the it to initiate an object of the dex maker class
    private DexMakerWrapper() {
        this.dexMaker = new DexMaker();
    }

    // set class name as well as start class declaration
    public DexMakerWrapper setClassName(String ClassName) {
        this.ClassName = ClassName;
        TypeId<?> classDeclaration = TypeId.get("L" + this.ClassName + ";");
        this.dexMaker.declare(classDeclaration, this.ClassName + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
        return this;
    }

    // set class fields with data type String for all of them
    public DexMakerWrapper setStringFields(ArrayList<String> Fields) {
        TypeId<System> systemType = TypeId.get(System.class);
        TypeId<String> DataType = TypeId.get(String.class);

        for (String field : Fields) {
            FieldId<System, String>FieldDec = systemType.getField(DataType, field);
            this.dexMaker.declare(FieldDec, Modifier.PUBLIC, null);
        }
        return this;
    }

    // creating the class itself 
    public Class<?> createClass() {
        File outputDir = new File(".");
        try {
            ClassLoader loader = this.dexMaker.generateAndLoad(DexMakerWrapper.class.getClassLoader(), outputDir);
            this.createdClass = loader.loadClass(this.ClassName);
            return this.createdClass;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}

Here is an example of my use of previous code

val fields = ArrayList<String>()
fields.add("A")
fields.add("B")
fields.add("C")
fields.add("D")
val dex: DexMakerWrapper = DexMakerWrapper.Constructor().setClassName("ModelA").setStringFields(fields)
val createdClass = dex.createClass()

I'm getting a crash java.lang.RuntimeException

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.kn.testcreatingaclassinruntime/com.kn.testcreatingaclassinruntime.MainActivity}: java.lang.IllegalStateException: Undeclared type Ljava/lang/System; declares members: [Ljava/lang/System;.A, Ljava/lang/System;.B, Ljava/lang/System;.C, Ljava/lang/System;.D] []

The crash is happening inside your code DexMaker class inside generate function

Any help would be appreciated.

@hamada147
Copy link
Author

Another update.
For some reason or another I'm unable to figure out the reason for the crash except the following information.
Ljava/lang/system is no declared.
in TypeDeclaration class declared variable value is always false on it why no idea. I'm also clueless as of what changes this value or what set it.
So I changed my Wrapper

package com.kn.testcreatingaclassinruntime.CreateClassInRunTimeWrapper;

import android.content.ContextWrapper;

import com.android.dx.DexMaker;
import com.android.dx.FieldId;
import com.android.dx.TypeId;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;

/**
 * This is a DexMaker wrapper to create classes in run time
 * because the existing code is way too big and confusing for me
 * @author Ahmed Moussa <email>[email protected]</email>
 * @version 1.0
 * Created by Ahmed Moussa on 7/2/18.
 */
public final class DexMakerWrapper {

    // ClassName
    private String ClassName;
    // dexMaker used to create the class
    private DexMaker dexMaker;
    // class declaration code
    private TypeId<?> classDeclaration;
    // class declaration after finishing
    private Class<?> createdClass;

    // make and entry point to the train methodology
    public static DexMakerWrapper Constructor() {
        return new DexMakerWrapper();
    }

    // use the it to initiate an object of the dex maker class
    private DexMakerWrapper() {
        this.dexMaker = new DexMaker();
    }

    // set class name as well as start class declaration
    public DexMakerWrapper setClassName(String ClassName) {
        this.ClassName = ClassName;
        this.classDeclaration = TypeId.get("Lcom/kn/testcreatingaclassinruntime/CreateClassInRunTimeWrapper/" + this.ClassName + ";");
        this.dexMaker.declare(classDeclaration, this.ClassName + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
        return this;
    }

    // set class fields with data type String for all of them
    public DexMakerWrapper setStringFields(ArrayList<String> Fields) {
        // TypeId<System> systemType = TypeId.get(System.class);
        TypeId<String> DataType = TypeId.get(String.class);

        for (String field : Fields) {
            FieldId<System, String> FieldDec = (FieldId<System, String>) this.classDeclaration.getField(DataType, field); //systemType.getField(DataType, field);
            this.dexMaker.declare(FieldDec, Modifier.PUBLIC, null);
        }
        return this;
    }

    // creating the class itself
    public Class<?> createClass(File f) {

        File outputDir = f;
        try {
            ClassLoader loader = this.dexMaker.generateAndLoad(DexMakerWrapper.class.getClassLoader(), outputDir);
            this.createdClass = loader.loadClass(this.ClassName);
            return this.createdClass;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}

And my usage

val fields = ArrayList<String>()
        fields.add("A")
        fields.add("B")
        fields.add("C")
        fields.add("D")

        var dir = this.dataDir
        val createdClass = DexMakerWrapper.Constructor().setClassName("ModelA").setStringFields(fields).createClass(dir)
        for (f in createdClass!!.declaredFields) {
            println(f.name)
        }

Also had to add in the manifest file.

Now I'm getting
java.lang.ClassNotFoundException: Didn't find class "ModelA" on path: DexPathList[[zip file "/data/user/0/com.kn.testcreatingaclassinruntime/Generated_-1884772861.jar"],nativeLibraryDirectories=[/system/lib, /vendor/lib]]

Now what did I do wrong here?

@hamada147
Copy link
Author

Example code itself is not running?

@hamada147
Copy link
Author

Guys,
Any help would be deeply appreciated.
@drewhannay @moltmann

@hamada147
Copy link
Author

Managed to test a fix.

// this line should include the absolute path of the class
this.createdClass = loader.loadClass("com.kn.testcreatingaclassinruntime.CreateClassInRunTimeWrapper." + this.ClassName);

@hamada147 hamada147 reopened this Jul 5, 2018
@hamada147
Copy link
Author

So Far I'm able to create the Class in run time as shown in the code. With the wanted structure

public class ModelA {
    public String value1;
    public String value2;
    public String value3;
}

But I'm not able to create new instant out of it and therefore I'm not able to set fields value which is what I want

I need to make an ArrayList of this newly generated Class and set the values inside their fields.

@drewhannay @moltmann

@hamada147
Copy link
Author

hamada147 commented Jul 5, 2018

@abti

@hamada147
Copy link
Author

@swankjesse

@hamada147
Copy link
Author

@allight

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

No branches or pull requests

1 participant