-
Notifications
You must be signed in to change notification settings - Fork 8
Same-Class Lambdas #187
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
Merged
stanhebben
merged 17 commits into
feature/refactor
from
feature/refactor-enhancements/same-class-lambdas
Feb 28, 2025
Merged
Same-Class Lambdas #187
stanhebben
merged 17 commits into
feature/refactor
from
feature/refactor-enhancements/same-class-lambdas
Feb 28, 2025
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This ensures Fernflower doesn't complain about invalid method names. This change might be reverted in the future. Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
This module will house all runtime-executing components for the Java integration of the language. In other words, if any compiler class needs to be present at runtime for scripts or other code to be executed (NOT compiled), then it will be located into this module. Examples include reflection utilities, custom classes that might be used by Java reflection to inspect scripts, INDY factories etc. Essentially, assuming a script has already been compiled to bytecode, this module should contain the code needed to allow the script to run (excluding any additional environment-provided classes). Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
- Compile them as just another private method in the same class This means lambdas cannot be invoked (if not through reflection) and they have access to all public, default, private, protected members without any access restrictions. - Use INDY to generate the actual lambda implementation class at runtime This allows us to change the algorithm at any point without having to touch the compiler, allowing for an easier language evolution over time. Moreover, it also allows us to put the various classes in the correct package and, potentially, as nest-mates of the owner, bypassing all access restrictions. Furthermore, it gives us flexibility for compatibility with newer Java versions. Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
- Fully support CONDY - Move them to their own package Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
fbaa253 to
82f2117
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Warning
This PR depends on #186 and ZenCodeLang/StdLibs#13. Those PRs must be merged first to ensure proper functionality.
In a Nutshell
Finally this works!
Description
This PR brings a big set of changes with respect to how lambdas are compiled and, as sort of the groundwork for that, expands the capabilities of the compiler with both a new module and the ability to leverage Java 7's
invokedynamicinstruction (and potentiallyConstantDynamic? Though I haven't tested that as we don't need it yet). The full list of changes will be described in the paragraphs that follow.JavaRuntime
JavaRuntimeis a new module that has been added and, as the name suggests, it aims to be the runtime counterpart toJavaBytecodeCompilerand in general of the Java compiler for the ZenCode language. Its goal is to provide a place where all components of the language that need to exist at runtime to ensure proper execution of scripts can be placed. Examples include reflection utilities, a potentialZenTypein the future to ensure proper reification with Java integration, andinvokedynamicutilities.In general, the idea is to assume that precompiled programs (or scripts) could be executed on the JVM without any other module but
JavaRuntime.invokedynamicsupport inJavaWriterJavaWriterhas been expanded to provide a set of utilities that allow processinginvokedynamicinstructions andConstantDynamictypes1. Essentially,JavaWriter#invokeDynamiccan now be used to write aninvokedynamicinstruction into bytecode, whileJavaWriter#constant(andJavaWriter#ldc) support dynamic constants viaJavaCondy.All the details representing the call site2 and the BSM3 are represented by classes in the
indypackage, respectivelyJavaIndy/JavaCondyandBsmData. They are all constructed via builders and provide immediate validation.invokedynamic-based same-class lambdasThe major change introduced by this PR: lambdas are now compiled as methods located within the same class that defines them and the corresponding functional interface instance is dynamically generated at runtime through
invokedynamicandLambdaFactory. This brings various advantages:IllegalAccessErrorthat plagued the language previously4.invokedynamicmeans that the generation of classes implementing lambdas is done at runtime with an algorithm that can be evolved separately from the compiler; in turn this means that if we were to find alternatives to class generation, potentially precompiled programs would benefit from it without requiring recompilation.invokedynamicensures that lambdas are only generated when needed, so if a particular code path involving a lambda is never invoked, the corresponding class won't exist, saving up space.Unfortunately, this also brings a disadvantage. Namely FernFlower isn't really able to understand the
invokedynamicmethod, and thus it replaces it with a very... peculiar instruction. Nevertheless, the general code flow is still understandable, so I consider this a minor issue.Minor changes
This PR also brings some more minor improvements, which do not warrant a separate section and are thus described here.
invokedynamiclambdas, converting between functional interfaces now also goes throughLambdaFactory, cleverly reusing it and thus reducing code duplication.JavaClasscan now be constructed via aClassfile, simplifying usage with Java classes.$capture$to avoid conflicts, and the receiver ($this).Footnotes
ConstantDynamicis not supported in Java 8: it was added around Java 11. So technically all code supporting it is useless as of now, but I figured we might as well have the framework for it ready to leverage it in the future if we decide to either migrate to newer Java versions or have Java-version-dependent compilation. ↩The call site is essentially the pair representing the method name and the descriptor of the method that will be dynamically invoked by the
invokedynamicinstruction. ↩A bootstrap method (or BSM for short) represents a method that is invoked by the JVM when an
invokedynamicorConstantDynamicis found, allowing for the construction of the dynamic method call or constant respectively. ↩Unfortunately, due to limitations in the APIs used due to Java 8, the class needs to be declared
publicregardless, otherwise anIllegalAccessErroroccurs. This could be worked around, but the API for it is not only internal to the JDK (Java 15 provides Hidden Classes as API, which is what we want), but also has been removed in newer Java versions. ↩Speaking of, if someone can fix the IntelliJ based run, that'd be a great help! 😅 ↩