[Java/Dalvik] Compileroptimalisaties

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik ben al een tijdje bezig een bepaalde Android applicatie 'opnieuw' te maken, met de bedoeling dat hij open-source wordt.

Van Android weet ik wel genoeg af om dit te laten slagen, maar soms heb ik het gevoel dat ik bepaalde 'code' verkeerd omzet vanaf de gedecompilede versie, hier een voorbeeld:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
                Class myClass = this.getClass();
                Field field = myClass.getField(localName);
                Object currentField = field.get(this);
                myClass = TestObject.class;
                if(myClass.isAssignableFrom(field.getType())) {
                    return;
                } else {
                    if(currentField != null) {
                        return;
                    }
                    else {
                        Constructor[] constr = field.getType().getConstructors();
                        for(Constructor constructor : constr) {
                            Object object = constructor.newInstance(new Object());
                            if(object instanceof TestObject) {
                                mChildResult = (TestObject)object;
                                field.set(this, mChildResult);
                            }
                        }
                    }
                }
            }


Met als volgende SMALI code (Op zich logisch wat alles daar betekent):

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
   invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;

    move-result-object v4

    invoke-virtual {v4, p1}, Ljava/lang/Class;->getField(Ljava/lang/String;)Ljava/lang/reflect/Field;

    move-result-object v3

    .line 28
    .local v3, field:Ljava/lang/reflect/Field;
    invoke-virtual {v3, p0}, Ljava/lang/reflect/Field;->get(Ljava/lang/Object;)Ljava/lang/Object;

    move-result-object v1

    .line 29
    .local v1, currentField:Ljava/lang/Object;
    const-class v4, Lcom/test/TestObject

    invoke-virtual {v3}, Ljava/lang/reflect/Field;->getType()Ljava/lang/Class;

    move-result-object v5

    invoke-virtual {v4, v5}, Ljava/lang/Class;->isAssignableFrom(Ljava/lang/Class;)Z

    move-result v4

    if-eqz v4, :cond_9

    .line 30
    if-nez v1, :cond_9

    .line 31
    invoke-virtual {v3}, Ljava/lang/reflect/Field;->getType()Ljava/lang/Class;

    move-result-object v4

    invoke-virtual {v4}, Ljava/lang/Class;->getConstructors()[Ljava/lang/reflect/Constructor;

    move-result-object v0

    .line 32
    .local v0, constr:[Ljava/lang/reflect/Constructor;,"[Ljava/lang/reflect/Constructor<*>;"
    const/4 v4, 0x0

    aget-object v4, v0, v4

    const/4 v5, 0x0

    new-array v5, v5, [Ljava/lang/Object;

    invoke-virtual {v4, v5}, Ljava/lang/reflect/Constructor;->newInstance([Ljava/lang/Object;)Ljava/lang/Object;

    move-result-object v4

    check-cast v4, Lcom/test/TestObject

    iput-object v4, p0, Lcom/test/TestObject>mChildResult:Lcom/test/TestObject;

    .line 33
    iget-object v4, p0, Lcom/test/TestObject;->mChildResult:Lcom/test/TestObject;

    invoke-virtual {v3, p0, v4}, Ljava/lang/reflect/Field;->set(Ljava/lang/Object;Ljava/lang/Object;)V

    .line 34
    iput-object p1, p0, Lcom/test/TestObject;->mChildName:Ljava/lang/String;
    :try_end_41


Maar, ik vermoed niet dat de mensen die de applicatie hebben geschreven dit hebben gedaan. Want, het lijkt me nogal een kromme manier van het probleem oplossen.

Is dit misschien net als dat je
Java:
1
String test = "Test" + "Test";


Het volgende krijgt als je het hebt gecompiled (Om toch die String addities op te lossen?)

Java:
1
2
3
4
StringBuilder builder = new StringBuilder();
builder.append("Test");
builder.append("Test");
String test = builder.toString();


Want, het zou netter zijn om het dan op de eerste manier te kunnen programmeren. Dit is ook niet iets wat je echt op Google kan zoeken, aangezien ik geen idee op wat voor manier dit een optimalisatie / hack kan zijn om bepaalde dingen te laten werken.

Iemand een idee?

Acties:
  • 0 Henk 'm!

  • boe2
  • Registratie: November 2002
  • Niet online

boe2

'-')/

Lijkt me niet ZO vreemd aangezien een stringbuilder wel degelijk sneller is dan de eerste manier. Zou het wel netjes vinden als de compiler dit automatisch omzet :)

'Multiple exclamation marks,' he went on, shaking his head, 'are a sure sign of a diseased mind.' - Pratchett.


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Boeboe schreef op woensdag 06 januari 2010 @ 16:18:
Lijkt me niet ZO vreemd aangezien een stringbuilder wel degelijk sneller is dan de eerste manier. Zou het wel netjes vinden als de compiler dit automatisch omzet :)
Uhuh? De eerste manier wordt door Java automatisch omgezet naar de tweede manier. Dat is het punt niet, maar ik vroeg me af of er nog meer van dat soort dingen zijn, die als uitkomst mijn bovenste javacode hebben (Dus degene met myClass.isAssignableFrom, etc)

[ Voor 5% gewijzigd door Verwijderd op 06-01-2010 16:24 ]


Acties:
  • 0 Henk 'm!

  • Nick_S
  • Registratie: Juni 2003
  • Nu online

Nick_S

++?????++ Out of Cheese Error

Ik mis een beetje de context van het bovenste stukje code. Gebeurt dit binnen een bepaalde functie?

'Nae King! Nae quin! Nae Laird! Nae master! We willna' be fooled agin!'


Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Verwijderd schreef op woensdag 06 januari 2010 @ 16:24:
Uhuh? De eerste manier wordt door Java automatisch omgezet naar de tweede manier.
Wordt dit specifieke voorbeeld niet omgezet in 'TestTest'? ;)

't Is overigens vrij lastig om code correct te decompilen, want er zijn verschillende constructies die uiteindelijk in principe dezelfde java-bytecode kunnen opleveren. Loops en dergelijke worden in de java-stackmachine uiteindelijk allemaal ruwweg dezelfde soort jump's en hoewel dat vast wel te herkennen is omdat ze allemaal net iets andere volgordes hebben, kan het dus wel zo zijn dat ze uiteindelijk niet hetzelfde gedecompileerd worden als wat je erin compileerd. Bij compilen verdwijnt gewoon informatie.

Acties:
  • 0 Henk 'm!

  • Nick_S
  • Registratie: Juni 2003
  • Nu online

Nick_S

++?????++ Out of Cheese Error

Verwijderd schreef op woensdag 06 januari 2010 @ 16:24:
[...]


Uhuh? De eerste manier wordt door Java automatisch omgezet naar de tweede manier. Dat is het punt niet, maar ik vroeg me af of er nog meer van dat soort dingen zijn, die als uitkomst mijn bovenste javacode hebben (Dus degene met myClass.isAssignableFrom, etc)
De compiler van Java is vrij om te doen en te laten wat hij maar wilt, zolang het resultaat maar hetzelfde blijft volgens het Java Memory Model. Statements mogen zelfs omgedraaid worden. De JIT compiler is nog een veel leuker feest. Java code kan in de loop van de tijd (dus tijdens runtime) sneller worden hierdoor, vandaar dat je in benchmarks ook altijd een rampup ziet.

'Nae King! Nae quin! Nae Laird! Nae master! We willna' be fooled agin!'


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ACM schreef op woensdag 06 januari 2010 @ 16:31:
[...]

Wordt dit specifieke voorbeeld niet omgezet in 'TestTest'? ;)

't Is overigens vrij lastig om code correct te decompilen, want er zijn verschillende constructies die uiteindelijk in principe dezelfde java-bytecode kunnen opleveren. Loops en dergelijke worden in de java-stackmachine uiteindelijk allemaal ruwweg dezelfde soort jump's en hoewel dat vast wel te herkennen is omdat ze allemaal net iets andere volgordes hebben, kan het dus wel zo zijn dat ze uiteindelijk niet hetzelfde gedecompileerd worden als wat je erin compileerd. Bij compilen verdwijnt gewoon informatie.
Haha Ja. Als je het uitvoert wordt het inderdaad TestTest, ik bedoelde meer in het .class bestand zelf ;).
Heel veel loops die ik al gehad heb lijken precies hetzelfde, maar zijn als je ze programmeert op een bepaalde manier 'logisch' te implementeren, zodat ze er in de Java code eigenlijk compleet anders uitzien. Daar komt bij dat Dalvik ipv de stack registers gebruikt, dus opzich niet dezelfde limitaties heeft.

Ik zou de context wel in zijn geheel uitleggen.

Je kan deze klasse zien als een soort van XML Document / Object Tree. Er is een bepaald startobject, en deze heeft een aantal childs van dezelfde type. En dit is dan code logic om een bepaald element te starten.

Voor het gemak even op pastebin de hele klasse neergezet.

http://pastebin.com/m6f6ee0bd

Daarom bedacht ik me, misschien is er wel een hele bekende 'transformatie' die ik over mijn hoofd zie (De String + String -> StrinBuilder transformatie bijvoorbeeld), zodat ik die kan implementeren.

Of, ze hebben het gewoon zo doodleuk geschreven, en dan snap ik de programmeurs niet bepaald ;)

NOTE: Even voordat het te ver gaat over de JVM, dit gaat over de Dalvik Virtual Machine. Niet over de JVM, aangezien ik een Android Applicatie gedecompiled heb.

[ Voor 6% gewijzigd door Verwijderd op 06-01-2010 16:42 ]


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:39
Verwijderd schreef op woensdag 06 januari 2010 @ 16:24:
Uhuh? De eerste manier wordt door Java automatisch omgezet naar de tweede manier.
Even mierenneuken, maar: in jouw voorbeeld wordt helemaal geen StringBuilder gebruikt, want het betreft daar twee constanten, en de compiler doet al constant folding (dat is verplicht, voor zover ik weet). De StringBuilder wordt wel gebruikt als het om variabelen gaat.

In je concrete voorbeeld: in wat voor methode/klasse heb je dit gevonden? De vertaling van assembly naar Java lijkt me grotendeels correct (in ieder geval de eerste 10 regels, toen was ik 't beu), hoewel je op het eind een assignment aan mChildName mist.

Wat de methode doet, is een waarde aan een attribuut toekennen op basis van de naam van het attribuut (gegeven als een string). De vraag is dan of de programmeurs daadwerkelijk de reflection API hebben gebruikt om dat te implementeren, of dat de code gegenereerd wordt. Ik zou zo 1-2-3 geen high-level Java constructie weten die hier naar zou moeten mappen dus ik vermoed haast dat de code zelf geschreven is, of Android moet Java extensies toevoegen die hier iets mee doen.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Soultaker schreef op woensdag 06 januari 2010 @ 16:45:
[...]

Even mierenneuken, maar: in jouw voorbeeld wordt helemaal geen StringBuilder gebruikt, want het betreft daar twee constanten, en de compiler doet al constant folding (dat is verplicht, voor zover ik weet). De StringBuilder wordt wel gebruikt als het om variabelen gaat.
Haha, dank je wel, nuttige informatie voor het vervolg van dit leuke projectje :p
In je concrete voorbeeld: in wat voor methode/klasse heb je dit gevonden? De vertaling van assembly naar Java lijkt me grotendeels correct (in ieder geval de eerste 10 regels, toen was ik 't beu), hoewel je op het eind een assignment aan mChildName mist.
Een assignment aan mChildName mist? Deze?

code:
1
2
    .line 34
    iput-object p1, p0, Lcom/hyves/android/service/api/result/HyvesApiResult;->mChildName:Ljava/lang/String;

mChildName wordt hier niet echt veranderd, dus is die assignment niet meer nodig (Hij zet hem alleen eerst even leuk om in een register variabele).. tenminste, dat is als ik het goed lees?
Wat de methode doet, is een waarde aan een attribuut toekennen op basis van de naam van het attribuut (gegeven als een string). De vraag is dan of de programmeurs daadwerkelijk de reflection API hebben gebruikt om dat te implementeren, of dat de code gegenereerd wordt. Ik zou zo 1-2-3 geen high-level Java constructie weten die hier naar zou moeten mappen dus ik vermoed haast dat de code zelf geschreven is, of Android moet Java extensies toevoegen die hier iets mee doen.
Dat is in ieder geval goed nieuws. Misschien hebben ze het inderdaad nu wel zo geschreven. Gelukkig is het tot nu toe maar een enkele klasse waarbij ze het zo hebben gedaan, de rest is redelijk logisch geschreven.

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:39
Is er trouwens geen echte decompiler voor Dalvik code? Voor Java classes kan dat o.a. met jad, voor .NET bytecode o.a. met Reflector, en deze bytecode ziet er op zich ook behoorlijk eenvoudig uit. Ik zou het handmatig decompileren in ieder geval vrij snel zat worden (om maar niet te spreken over kleine foutjes die er waarschijnlijk vroeg of laat insluipen).

[ Voor 32% gewijzigd door Soultaker op 06-01-2010 16:59 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Was het maar zo'n feest, dan was ik al weer klaar. Dit is op dit moment degene die het meest dichtbij de 'echte source' komt, en dit is nog relatief makkelijk om te zetten. (Het is alleen wel veel werk om echt 600 klassen zo om te zetten, maar het is wel leuk werk). Ik ben ook heel erg blij dat de namen van bepaalde variabelen behouden worden, dat maakt het gelukkig een stuk eenvoudiger ;) Ja, ik ben ook heel bang voor fouten. Maar, gelukkig weet ik wel heel veel van Android code, dus het debuggen ervan zou niet al te moeilijk zijn, en ik zou geen structurele fouten maken als alles 'complieert' (dit zegt natuurlijk nog niets, off-by-one fouten e.d. kunnen er altijd zijn ;)

Dalvik zit sowieso nog in de kinderschoenen, en er zijn nog geen goede (iig publieke) manieren om een hele applicatie uit te lezen (Aangezien er veel gebruik wordt gemaakt van resources met ids). Hier heb ik gelukkig nu wel wat voor gemaakt. Maar, een echte decompiler bestaat (Na heel uitgebreid / lang zoeken) nog niet echt.

[ Voor 25% gewijzigd door Verwijderd op 06-01-2010 17:04 ]


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:39
Trouwens, nog even hier over:
Verwijderd schreef op woensdag 06 januari 2010 @ 16:52:
Een assignment aan mChildName mist? Deze?
code:
1
2
    .line 34
    iput-object p1, p0, Lcom/hyves/android/service/api/result/HyvesApiResult;->mChildName:Ljava/lang/String;

mChildName wordt hier niet echt veranderd, dus is die assignment niet meer nodig (Hij zet hem alleen eerst even leuk om in een register variabele).. tenminste, dat is als ik het goed lees?
Volgens mij komt die regel neer op mChildName = localName. Verder wordt mChildName nergens anders toegekend, terwijl er in endElement() vanuit wordt gegaan dat als mChildNode niet null is, mChildName dat óók niet is, dus ik vermoed dat je die assignment toch nodig hebt. ;)

(Disclaimer: ik heb nooit wat met Android gedaan, alleen een Google talk erover gehoord twee jaar geleden.)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ahaha, je hebt gelijk. Dank je wel. Anders had ik het vast nooit gevonden! :D Pff, soms doe ik toch dingen niet helemaal goed ;)

Acties:
  • 0 Henk 'm!

  • Nick_S
  • Registratie: Juni 2003
  • Nu online

Nick_S

++?????++ Out of Cheese Error

Even iets anders, je weet dat als je de source decompiled en deze zelf opnieuw maakt, dat er nog steeds copyright op rust en je dus deze niet opensource mag maken?

Tenminste, dat is zoals ik het altijd begrepen heb. Je mag wel een beschrijving maken en iemand anders het laten implementeren, maar implementeren aan de hand van gedecompilede code mag volgens mij niet.

'Nae King! Nae quin! Nae Laird! Nae master! We willna' be fooled agin!'


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Nick_S schreef op donderdag 07 januari 2010 @ 01:03:
Even iets anders, je weet dat als je de source decompiled en deze zelf opnieuw maakt, dat er nog steeds copyright op rust en je dus deze niet opensource mag maken?

Tenminste, dat is zoals ik het altijd begrepen heb. Je mag wel een beschrijving maken en iemand anders het laten implementeren, maar implementeren aan de hand van gedecompilede code mag volgens mij niet.
Dit mocht van de ontwikkelaars. ;) Ik heb het ze expliciet zo gevraagd, dus dat zit wel goed.
Ik heb een email om dit te bewijzen later. Natuurlijk heet de klasse niet echt TestObject, maar heeft het een project-specifieke naam, maar dat is ook een beetje om deze reden.
Pagina: 1