-
Notifications
You must be signed in to change notification settings - Fork 8
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
Same-Class Lambdas #187
Open
TheSilkMiner
wants to merge
18
commits into
feature/refactor
Choose a base branch
from
feature/refactor-enhancements/same-class-lambdas
base: feature/refactor
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Same-Class Lambdas #187
TheSilkMiner
wants to merge
18
commits into
feature/refactor
from
feature/refactor-enhancements/same-class-lambdas
+2,093
−842
Conversation
This file contains 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
Use version catalogues to keep versions in sync Update to gradle 8.8
This ensures Fernflower doesn't complain about invalid method names. This change might be reverted in the future. Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
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 <[email protected]>
- 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 <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
Signed-off-by: TheSilkMiner <[email protected]>
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
invokedynamic
instruction (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
JavaRuntime
is a new module that has been added and, as the name suggests, it aims to be the runtime counterpart toJavaBytecodeCompiler
and 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 potentialZenType
in the future to ensure proper reification with Java integration, andinvokedynamic
utilities.In general, the idea is to assume that precompiled programs (or scripts) could be executed on the JVM without any other module but
JavaRuntime
.invokedynamic
support inJavaWriter
JavaWriter
has been expanded to provide a set of utilities that allow processinginvokedynamic
instructions andConstantDynamic
types1. Essentially,JavaWriter#invokeDynamic
can now be used to write aninvokedynamic
instruction 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
indy
package, respectivelyJavaIndy
/JavaCondy
andBsmData
. 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
invokedynamic
andLambdaFactory
. This brings various advantages:IllegalAccessError
that plagued the language previously4.invokedynamic
means 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.invokedynamic
ensures 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
invokedynamic
method, 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.
invokedynamic
lambdas, converting between functional interfaces now also goes throughLambdaFactory
, cleverly reusing it and thus reducing code duplication.JavaClass
can now be constructed via aClass
file, simplifying usage with Java classes.$capture$
to avoid conflicts, and the receiver ($this
).Footnotes
ConstantDynamic
is 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
invokedynamic
instruction. ↩A bootstrap method (or BSM for short) represents a method that is invoked by the JVM when an
invokedynamic
orConstantDynamic
is 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
public
regardless, otherwise anIllegalAccessError
occurs. 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! 😅 ↩