Welche Scala-Anweisungen oder Code kann einen Byte-Code erzeugen, der nicht nach Java übersetzt werden kann?

8

Ich habe eine Antwort auf eine Frage zur Konvertierung von Scala-Code in Java-Code gelesen. Es sagt:

Ich glaube nicht, dass es möglich ist, von Scala zurück in Standard-Java zu konvertieren, da Scala eine ziemlich niedrige Byte-Code-Manipulation durchführt. Ich bin 90% sicher, dass sie einige Dinge tun, die nicht genau in normalen Java-Code übersetzt werden können.

Also welche Scala-Anweisungen oder Code kann Bytecode erzeugen, der nicht in Java übersetzt werden kann?

P.S. Ich stimme dieser Antwort grundsätzlich zu, möchte aber ein konkretes Beispiel für Lernzwecke.

    
Cherry 04.07.2014, 04:07
quelle

6 Antworten

8

Die Antwort hängt wirklich davon ab, wie schwer Sie versuchen möchten, den Code zu konvertieren.

Da Java und Scala beide vollständig sind, kann jedes Programm in einem in das andere konvertiert werden, aber das ist nicht wirklich interessant oder nützlich.

Was Sie wirklich wollen, ist, die Ergebnisse in lesbaren, idiomatischen Code zu konvertieren. Aus dieser Perspektive kann selbst Java-Code nicht automatisch nach Java konvertiert werden, da die Kompilierung Informationen verliert (obwohl sie im Vergleich zu C relativ wenig ist) und Maschinen sind sowieso nicht so gut wie Menschen beim Schreiben von lesbarem Code.

Wenn Sie einen Java- und Scala-Experten haben, könnten sie wahrscheinlich Ihre Scala-Codebasis in Java umschreiben und am Ende mit einigermaßen idiomatischem Java-Code enden. Aber es wäre nicht so gut lesbar wie Scala, weil Scala eine Sprache ist, die Java verbessern soll. Scala versucht, die Warzen aus Java zu entfernen und bietet mächtige High-Level-Programmierfunktionen, die die Notwendigkeit für alle klassischen Java-Boilerplate beseitigen. Daher ist die Java-äquivalente Codebase nicht so lesbar.

Aus dieser Perspektive lautet die Antwort "jede Funktion in Scala, die nicht in Java ist".

    
Antimony 04.07.2014 04:39
quelle
6

Scalas verschachtelte Blöcke haben kein Java-Äquivalent.

Verschachtelter Block in Scala (übernommen von diese Frage ):

%Vor%

Erzeugt den Bytecode

%Vor%

Bei Befehl 0 wird ein nicht initialisiertes Objekt auf den Stapel geschoben und dann bei Befehl 10 initialisiert. Zwischen diesen beiden Punkten erfolgt ein Rückwärtssprung von 6 nach 5. Dies deckt tatsächlich einen Fehler im OpenJDK-Bytecode-Verifizierer auf, wenn er dies ablehnt trotz der Tatsache, dass es von den JVM-Spezifikationen akzeptiert wird. Dies wurde wahrscheinlich durch Tests bestanden, da dieser Bytecode nicht aus Java generiert werden kann.

Da in Java verschachtelte Blöcke keine Ausdrücke sind, die einen Wert ergeben, wäre das nächstliegende Java-Äquivalent

%Vor%

Was würde zu etwas ähnlich wie

kompilieren %Vor%

Beachten Sie, dass das nicht initialisierte Objekt auf dem Stapel zum Zeitpunkt des Rückwärtssprungs nicht vorhanden ist. (N.B. Bytecode wurde von Hand geschrieben, nicht ausführen!)

Diese Veröffentlichung von Li, White und Singer zeigt Unterschiede in den JVM-Sprachen einschließlich des Bytecodes, zu dem sie kompilieren. Es stellt fest, dass bei einer N-Gramm-Analyse von Bytecodes, dass 58,5% von 4-Gramm, die von Scala ausgeführt werden, nicht in Bytecode gefunden werden, der von Java ausgeführt wird. Das soll nicht heißen, dass Java diese Bytecodes nicht erzeugen kann, sondern dass sie im Java-Corpus nicht vorhanden sind.

    
ggovan 04.07.2014 16:26
quelle
2

Wie Sie bemerkt haben, kompiliert Scala schließlich den JVM-Bytecode. Eine naheliegende Anweisung aus dem JVM-Befehlssatz, der in der Java-Sprache kein Äquivalent hat, ist goto .

Ein Scala-Compiler könnte goto für verwenden, um Schleifen oder tail-rekursive Methoden zu optimieren. In diesem Fall müssten Sie in Java das Verhalten von goto emulieren.

Wie Antimon andeutet, kann eine vollständige Turing-Sprache zumindest eine andere Turing-Sprache wiedergeben. Das resultierende Programm kann jedoch schwer und suboptimal sein.

Als letzte Anmerkung können Decompiler helfen. Ich bin nicht vertraut mit den Eigenheiten von Decompilern, aber ich nehme an, dass sie sich sehr auf Muster verlassen. Ich meine zum Beispiel, Java-Quellmuster f (x) kompiliert zu Bytecode-Muster f '(x), also schaffen es einige mit harter Arbeit und Erfahrung, Bytecode f' (y) zu Java-Quelle f (y) zu dekompilieren. .

Allerdings habe ich noch nicht von Scala Decompilern gehört (vielleicht arbeitet jemand daran).

[EDIT] Was ich ursprünglich damit meinte, das Verhalten eines goto zu emulieren:

Ich hatte switch/case -Anweisungen innerhalb einer Schleife im Kopf, und cdshines zeigte eine andere Möglichkeit, indem ich in einer Schleife das Label break/continue verwendete (obwohl ich glaube, dass "ignorierte und verurteilte" Features nicht Standard ).

Um in diesen beiden Fällen zurück zu einer früheren Anweisung zu springen, ist eine idiomatische Java-Schleife ( for/while/do-while ) erforderlich (jede andere Anregung?). Eine Endlosschleife macht es einfach zu implementieren, eine bedingte Schleife würde mehr Arbeit erfordern, aber ich gehe davon aus, dass dies machbar ist.

Außerdem ist goto nicht auf Schleifen beschränkt. Um vorwärts zu springen würde Java andere Konstrukte benötigen.

Ein Gegenbeispiel: In C gibt es Einschränkungen, aber Sie müssen nicht so große Längen durchgehen, weil es eine Anweisung goto gibt.

Wenn Sie an nicht-idiomatischen Sprüngen in Scala interessiert sind, cf dieses alte Q & A von mir . Mein Punkt ist, dass nicht nur ein Scala-Compiler goto auf eine Art und Weise ausgibt, die in Java nicht selbstverständlich ist, aber ein Entwickler kann dies mit Hilfe von Scala-Makros genau kontrollieren.

LabelDef : Ein markierter Ausdruck. Nicht in Sprachsyntax ausdrückbar, sondern vom Compiler generiert, um while / do-while-Schleifen zu simulieren, und auch vom Pattern-Matcher. In meinen vergangenen Tests könnte es auch für Vorwärtssprünge verwendet werden. In Scala Internals haben die Entwickler geschrieben, dass LabelDef entfernt wurde, aber ich weiß nicht, ob und wann sie das tun würden.

Daher können Sie ja das Verhalten von goto in Java reproduzieren, aber wegen der Komplexität, die damit verbunden ist, würde ich das nicht standard Java nennen, IMHO. Vielleicht ist meine Formulierung falsch, aber in meinen Augen ist die Reproduktion eines elementaren Verhaltens durch komplexe Mittel eine "Nachahmung" dieses Verhaltens.

Prost.

    
eruve 04.07.2014 13:31
quelle
1

Es hängt wirklich davon ab, wie Sie definieren. Also, welche Scala-Anweisungen oder Code kann Bytecode erzeugen, der nicht in Java übersetzt werden kann? .

Letztendlich werden einige der Scala-Features von der so genannten ScalaSignature (Scala-Signatur) unterstützt, die Metainformationen speichert. Ab 2.10 kann es als secret api angesehen werden, das durch die Scala-Reflektionsmechanismen abstrahiert wird (die sich radikal von der Java-Reflektion unterscheiden). Die Dokumentation ist knapp, aber Sie können diese pdf nachsehen, um die Details zu erhalten (es könnte große Änderungen geben seit dem dann). Es gibt keine Möglichkeit, identische Strukturen in nativem Java zu erzeugen, es sei denn, Sie sind ein Backback-Tool zur Manipulation von Bytecodes.

In einem entspannteren Sinne gibt es Makros und Implikate, die ausschließlich mit einem Scalac interagieren und in Java kein direktes Analogon haben. Ja, Sie können Java-Code schreiben, der mit dem von scalac erzeugten Ergebnis identisch ist, aber Sie können diese dynamischen Anweisungen, die den Compiler leiten, nicht schreiben.

    
om-nom-nom 04.07.2014 07:15
quelle
1

Ich arbeite zufällig mit viel Byte-Code und ich schrieb eine Zusammenfassung von Byte-Code -Features die nicht reproduzierbar sind, indem Java-Code geschrieben wird. Alle diese nicht vorhandenen Features sind jedoch eher Konventionen zum Erstellen von Bytecode-Anweisungen. Mit Java 8, jedem vorhandenen Opcode wurde von einem Java-Klassen-Dateiformat verwendet. Dies ist nicht allzu überraschend, da die Java-Sprache die Entwicklung des Java-Byte-Code-Formats vorantreibt. Eine Ausnahme könnte die Anweisung INVOKEDYNAMIC sein, die eingeführt wurde, um dynamische Sprachen auf der JVM besser zu unterstützen, aber selbst diese Anweisung wird in Java 8 zum Implementieren von Lambda-Ausdrücken verwendet. Daher kann es Kombinationen / Anweisungen von Bytecode-Anweisungen geben, die nicht vom javac Compiler erzeugt werden, aber es gibt keine spezielle Anweisung, die nur von einer anderen JVM-Sprache verwendet wird.

Von den Byte-Code-Eigenschaften, die ich in der Zusammenfassung genannt habe, würde ich bemerkenswerter Weise sagen, dass nicht deklarierte checked Ausnahmen, ohne sie abzufangen ist eine Funktion, die von Scala, aber nicht von Java unterstützt wird. Ansonsten würde ich jedoch sagen, dass es keine Low-Level-Byte-Code-Manipulation von scalac gibt, die javac unbekannt ist. Meiner Erfahrung nach können die meisten Scala-Klassen auch explizit in Java geschrieben werden.

    
Rafael Winterhalter 05.07.2014 01:55
quelle
-4

Ich denke, es gibt keinen solchen Code. AFAIK gibt es nur eine jvm-Anweisung, die Java nicht generieren kann - invoke_dynamic. Diese Anweisung ist für dynamische Sprache und scala ist eine statische Sprache, was bedeutet, dass sie auch nicht generiert werden kann. So ist es möglich, Scala-Code in Java-Code und wahrscheinlich nicht lesbaren Java-Code zu übersetzen.

    
cloud 04.07.2014 04:25
quelle

Tags und Links