├── .github └── workflows │ ├── build.yml │ └── updateSubModules.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── logs └── latest.log ├── settings.gradle └── src ├── generator ├── java │ └── youyihj │ │ └── probezs │ │ └── generator │ │ └── MethodParameterNamesGenerator.java └── resources │ └── default.yaml ├── main ├── java │ └── youyihj │ │ └── probezs │ │ ├── Environment.java │ │ ├── ProbeZS.java │ │ ├── ProbeZSConfig.java │ │ ├── api │ │ ├── BracketHandlerService.java │ │ └── BracketHandlerServiceImpl.java │ │ ├── bracket │ │ ├── BracketHandlerCaller.java │ │ ├── BracketHandlerEntry.java │ │ ├── BracketHandlerEntryProperties.java │ │ ├── BracketHandlerMirror.java │ │ └── BracketHandlerResult.java │ │ ├── core │ │ ├── ASMMemberCollector.java │ │ ├── BytecodeClassLoader.java │ │ ├── CoreMod.java │ │ ├── LateMixinInit.java │ │ ├── ProbeZSClassTransformer.java │ │ ├── asm │ │ │ ├── ASMCraftTweakerAPI.java │ │ │ └── CraftTweakerAPIHooks.java │ │ ├── hook │ │ │ ├── ILocalVariableMethodOutput.java │ │ │ ├── LocalVariable.java │ │ │ ├── ParsedFunctionHooks.java │ │ │ ├── ParsedZenClassHooks.java │ │ │ └── Scope.java │ │ └── mixin │ │ │ ├── MixinEnvironmentMethod.java │ │ │ ├── MixinMethodOutput.java │ │ │ ├── MixinParsedFunction.java │ │ │ ├── MixinParsedZenClass.java │ │ │ ├── MixinStatementBlockOrForeach.java │ │ │ └── SymbolArgumentAccessor.java │ │ ├── docs │ │ ├── BracketReturnTypes.java │ │ └── ParameterNameMappings.java │ │ ├── member │ │ ├── AnnotatedMember.java │ │ ├── ExecutableData.java │ │ ├── FieldData.java │ │ ├── MemberFactory.java │ │ ├── ParameterData.java │ │ ├── asm │ │ │ ├── ASMAnnotatedMember.java │ │ │ ├── ASMField.java │ │ │ ├── ASMMemberFactory.java │ │ │ ├── ASMMethod.java │ │ │ ├── ASMParameter.java │ │ │ └── TypeResolver.java │ │ └── reflection │ │ │ ├── JavaExecutable.java │ │ │ ├── JavaField.java │ │ │ ├── JavaParameter.java │ │ │ └── ReflectionMemberFactory.java │ │ ├── render │ │ ├── RenderHelper.java │ │ └── RenderTaskDispatcher.java │ │ ├── tree │ │ ├── IHasImportMembers.java │ │ ├── IMaybeExpansionMember.java │ │ ├── IZenDumpable.java │ │ ├── JavaTypeMirror.java │ │ ├── ZenClassNode.java │ │ ├── ZenClassTree.java │ │ ├── ZenConstructorNode.java │ │ ├── ZenExecutableNode.java │ │ ├── ZenMemberNode.java │ │ ├── ZenOperatorNode.java │ │ ├── ZenParameterNode.java │ │ ├── ZenPropertyNode.java │ │ ├── global │ │ │ ├── ZenGlobalFieldNode.java │ │ │ ├── ZenGlobalMemberTree.java │ │ │ └── ZenGlobalMethodNode.java │ │ └── primitive │ │ │ ├── IPrimitiveType.java │ │ │ ├── ZenAnyNode.java │ │ │ ├── ZenBoolNode.java │ │ │ ├── ZenByteNode.java │ │ │ ├── ZenDoubleNode.java │ │ │ ├── ZenFloatNode.java │ │ │ ├── ZenIntNode.java │ │ │ ├── ZenIntRangeNode.java │ │ │ ├── ZenLongNode.java │ │ │ ├── ZenShortNode.java │ │ │ ├── ZenStringNode.java │ │ │ └── ZenVoidNode.java │ │ └── util │ │ ├── Arrays.java │ │ ├── DebugAPIAdapter.java │ │ ├── FileUtils.java │ │ ├── IndentStringBuilder.java │ │ ├── IntersectionType.java │ │ ├── LoadingObject.java │ │ ├── ZenKeywords.java │ │ └── ZenOperators.java └── resources │ ├── ProbeZS_at.cfg │ ├── assets │ └── base │ │ └── models │ │ └── item │ │ └── wrench.json │ ├── mappings │ ├── bracket-return-types.yaml │ └── method-parameter-names.yaml │ ├── mcmod.info │ ├── mixins.probezs.json │ └── pack.mcmeta └── test └── java └── youyihj └── probezs └── TypeResolverTest.java /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | 7 | jobs: 8 | generate: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout ProbeZS Repository 12 | uses: actions/checkout@v4 13 | with: 14 | submodules: recursive 15 | - name: Set up JDK 17 16 | uses: actions/setup-java@v3 17 | with: 18 | distribution: temurin 19 | java-version: 17 20 | - name: Setup Gradle 21 | uses: gradle/actions/setup-gradle@v4 22 | with: 23 | gradle-version: 8.1.1 24 | - name: Setup Decomp Workspace 25 | run: ./gradlew setupDecompWorkspace 26 | - name: Generate Parameter Name Mappings 27 | run: gradle generateParameterNameMappings --stacktrace 28 | - name: Upload Parameter Name Mappings 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: mappings 32 | path: generated 33 | build: 34 | runs-on: ubuntu-latest 35 | needs: generate 36 | steps: 37 | - name: Checkout ProbeZS Repository 38 | uses: actions/checkout@v4 39 | - name: Set up JDK 1.8 40 | uses: actions/setup-java@v3 41 | with: 42 | distribution: temurin 43 | java-version: 8 44 | - name: Download Parameter Name Mappings 45 | uses: actions/download-artifact@v4 46 | with: 47 | name: mappings 48 | path: generated 49 | - name: Setup Gradle 50 | uses: gradle/actions/setup-gradle@v4 51 | with: 52 | gradle-version: 8.1.1 53 | - name: Setup Decomp Workspace 54 | run: ./gradlew setupDecompWorkspace 55 | - name: Copy Parameter Name Mappings to Sources 56 | run: cp -f ./generated/method-parameter-names.yaml ./src/main/resources/mappings/ 57 | - name: Build with Gradle 58 | run: ./gradlew build 59 | - name: Get gradle version 60 | uses: EmberCM/action-get-gradle-version@master 61 | id: version 62 | with: 63 | file: gradle.properties 64 | - name: Upload ProbeZS JAR 65 | uses: actions/upload-artifact@v4 66 | with: 67 | name: jar 68 | path: build/libs/ProbeZS-${{ env.VERSION }}.jar 69 | 70 | deploy: 71 | runs-on: ubuntu-latest 72 | needs: build 73 | steps: 74 | - name: Checkout Github Pages Repository 75 | uses: actions/checkout@v4 76 | with: 77 | repository: friendlyhj/friendlyhj.github.io 78 | token: ${{ secrets.PAGE_TOKEN }} 79 | - name: Download Parameter Name Mappings 80 | uses: actions/download-artifact@v4 81 | with: 82 | name: mappings 83 | path: probezs-mappings 84 | - name: Commit Parameter Name Mappings Updates 85 | id: commit 86 | uses: EndBug/add-and-commit@v9 87 | with: 88 | author_name: friendlyhj 89 | author_email: youyi580@qq.com 90 | message: Update ProbeZS Parameter Name Mappings 91 | - name: Checkout ProbeZS Repository 92 | uses: actions/checkout@v4 93 | - name: Get gradle version 94 | uses: EmberCM/action-get-gradle-version@master 95 | id: version 96 | with: 97 | file: gradle.properties 98 | - name: Download ProbeZS JAR 99 | uses: actions/download-artifact@v4 100 | with: 101 | name: jar 102 | - name: Get Current Time 103 | id: time 104 | run: echo "NOW=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT" 105 | - name: Release 106 | if: steps.commit.outputs.committed == 'true' || github.event.head_commit.message != 'Update Submodules' 107 | uses: softprops/action-gh-release@v1 108 | with: 109 | name: Release ${{ env.VERSION }}-${{ steps.time.outputs.NOW }} 110 | tag_name: ${{ env.VERSION }}-${{ steps.time.outputs.NOW }} 111 | files: ProbeZS-${{ env.VERSION }}.jar 112 | 113 | -------------------------------------------------------------------------------- /.github/workflows/updateSubModules.yml: -------------------------------------------------------------------------------- 1 | name: Submodule Updates 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 3,0' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | name: Submodule update 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout ProbeZS Repository 14 | uses: actions/checkout@v3 15 | with: 16 | submodules: recursive 17 | token: ${{ secrets.PAGE_TOKEN }} 18 | fetch-depth: 0 19 | - name: Update Submodules 20 | run: git submodule update --remote 21 | - name: Commit 22 | uses: EndBug/add-and-commit@v9 23 | with: 24 | author_name: github-actions 25 | author_email: 41898282+github-actions[bot]@users.noreply.github.com 26 | message: Update Submodules 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | 16 | # gradle 17 | build 18 | .gradle 19 | 20 | # other 21 | eclipse 22 | run 23 | 24 | generated -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ModSources/crafttweaker"] 2 | path = ModSources/crafttweaker 3 | url = https://github.com/CraftTweaker/CraftTweaker.git 4 | branch = 1.12 5 | [submodule "ModSources/ZenUtils"] 6 | path = ModSources/ZenUtils 7 | url = https://github.com/friendlyhj/ZenUtils.git 8 | [submodule "ModSources/ModTweaker"] 9 | path = ModSources/ModTweaker 10 | url = https://github.com/jaredlll08/ModTweaker.git 11 | [submodule "ModSources/ContentTweaker"] 12 | path = ModSources/ContentTweaker 13 | url = https://github.com/CraftTweaker/ContentTweaker.git 14 | branch = develop/1.12 15 | [submodule "ModSources/CompatSkills"] 16 | path = ModSources/CompatSkills 17 | url = https://github.com/Coders-After-Dark/CompatSkills.git 18 | [submodule "ModSources/GregTech"] 19 | path = ModSources/GregTech 20 | url = https://github.com/GregTechCEu/GregTech.git 21 | [submodule "ModSources/Multiblocked"] 22 | path = ModSources/Multiblocked 23 | url = https://github.com/CleanroomMC/Multiblocked.git 24 | [submodule "ModSources/RandomTweaker"] 25 | path = ModSources/RandomTweaker 26 | url = https://github.com/Project-RT/RandomTweaker.git 27 | [submodule "ModSources/ModularMachinery-Community-Edition"] 28 | path = ModSources/ModularMachinery-Community-Edition 29 | url = https://github.com/NovaEngineering-Source/ModularMachinery-Community-Edition.git 30 | [submodule "ModSources/CraftTweakerIntegration"] 31 | path = ModSources/CraftTweakerIntegration 32 | url = https://github.com/TCreopargh/CraftTweakerIntegration.git 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 youyihj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProbeZS 2 | 3 | Dumps all ZenScript classes, methods and vanilla bracket handler entries into `*.dzs` files. 4 | 5 | It only targets at MC 1.12.2. And there are no plans to update to modern versions. 6 | 7 | Works with [ZenScript IntelliSense](https://marketplace.visualstudio.com/items?itemName=raylras.zenscript-intelli-sense) 8 | 9 | ## Code Generators 10 | 11 | Since Java compiled bytecodes don't retain parameter names by default. ProbeZS keeps a parameter name mapping by analysing source code. 12 | 13 | If you want to dump parameter names of another mod, please make a PR to add a submodule link the mod repo in ModSources directory. 14 | 15 | * You can add extra entries manually in `src/generator/resources/default.yaml` 16 | * You can add methods that aren't annotated `@ZenMethod` in `MethodParameterNamesGenerator.extras` 17 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar 2 | import org.jetbrains.gradle.ext.Gradle 3 | 4 | plugins { 5 | id("java") 6 | id("java-library") 7 | id("maven-publish") 8 | id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.7" 9 | id("eclipse") 10 | id("com.gtnewhorizons.retrofuturagradle") version "1.3.9" 11 | id("com.matthewprenger.cursegradle") version "1.4.0" 12 | } 13 | 14 | version = project.property("version") 15 | group = project.maven_group 16 | archivesBaseName = project.archives_base_name 17 | 18 | // Set the toolchain version to decouple the Java we run Gradle with from the Java used to compile and run the mod 19 | java { 20 | // toolchain { 21 | // languageVersion.set(JavaLanguageVersion.of(8)) 22 | // // Azul covers the most platforms for Java 8 toolchains, crucially including MacOS arm64 23 | // vendor.set(org.gradle.jvm.toolchain.JvmVendorSpec.AZUL) 24 | // } 25 | } 26 | 27 | tasks.withType(JavaCompile).configureEach { 28 | options.encoding = "UTF-8" 29 | } 30 | 31 | configurations { 32 | embed 33 | implementation.extendsFrom(embed) 34 | generatorImplementation.extendsFrom(implementation) 35 | } 36 | 37 | minecraft { 38 | mcVersion = '1.12.2' 39 | def args = ["-ea:${project.group}"] 40 | if (project.use_coremod.toBoolean()) { 41 | args << '-Dfml.coreMods.load=' + coremod_plugin_class_name 42 | } 43 | if (project.use_mixins.toBoolean()) { 44 | args << '-Dmixin.hotSwap=true' 45 | args << '-Dmixin.checks.interfaces=true' 46 | args << '-Dmixin.debug.export=true' 47 | } 48 | extraRunJvmArguments.addAll(args) 49 | 50 | useDependencyAccessTransformers = true 51 | 52 | injectedTags.put("VERSION", project.version) 53 | } 54 | 55 | // Generate a my.project.Tags class with the version number as a field 56 | tasks.injectTags.configure { 57 | outputClassName.set("${project.group}.Tags") 58 | } 59 | 60 | repositories { 61 | maven { 62 | url = 'https://maven.cleanroommc.com' 63 | } 64 | maven { url = "https://repo.spongepowered.org/maven" } 65 | //maven { url "https://maven.mcmoddev.com/" } 66 | maven { 67 | url "https://cursemaven.com" 68 | content { 69 | includeGroup "curse.maven" 70 | } 71 | } 72 | maven { 73 | name = 'blamejared' 74 | url = 'https://maven.blamejared.com' 75 | } 76 | mavenLocal() // Must be last for caching to work 77 | } 78 | 79 | dependencies { 80 | if (project.use_assetmover.toBoolean()) { 81 | implementation 'com.cleanroommc:assetmover:2.0' 82 | } 83 | if (project.use_mixins.toBoolean()) { 84 | implementation 'zone.rong:mixinbooter:7.0' 85 | } 86 | 87 | // Example deobf dependency 88 | // compileOnly rfg.deobf("curse.maven:endercore-231868:2972849:") 89 | 90 | if (project.use_mixins.toBoolean()) { 91 | api("org.spongepowered:mixin:0.8.3") { transitive = false } 92 | annotationProcessor('org.ow2.asm:asm-debug-all:5.2') 93 | annotationProcessor('com.google.guava:guava:24.1.1-jre') 94 | annotationProcessor('com.google.code.gson:gson:2.8.6') 95 | annotationProcessor("org.spongepowered:mixin:0.8.3") { transitive = false } 96 | } 97 | 98 | implementation rfg.deobf("CraftTweaker2:CraftTweaker2-MC1120-Main:1.12-4.1.20.685") 99 | implementation rfg.deobf("com.teamacronymcoders.base:base:1.12.2-3.13.0-SNAPSHOT.+") 100 | implementation rfg.deobf("com.teamacronymcoders:ContentTweaker:1.12.2-4.10.0") 101 | 102 | 103 | embed('org.yaml:snakeyaml:2.0') 104 | generatorImplementation 'fr.inria.gforge.spoon:spoon-core:10.4.2' 105 | 106 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0' 107 | testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0' 108 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0' 109 | 110 | } 111 | 112 | test { 113 | useJUnitPlatform() 114 | } 115 | 116 | def mixinConfigRefMap = 'mixins.' + project.archives_base_name + '.refmap.json' 117 | def mixinTmpDir = buildDir.path + File.separator + 'tmp' + File.separator + 'mixins' 118 | def refMap = "${mixinTmpDir}" + File.separator + mixinConfigRefMap 119 | def mixinSrg = "${mixinTmpDir}" + File.separator + "mixins.srg" 120 | 121 | if (project.use_mixins.toBoolean()) { 122 | tasks.named("reobfJar", ReobfuscatedJar).configure { 123 | extraSrgFiles.from(mixinSrg) 124 | } 125 | 126 | tasks.named("compileJava", JavaCompile).configure { 127 | doFirst { 128 | new File(mixinTmpDir).mkdirs() 129 | } 130 | options.compilerArgs += [ 131 | "-AreobfSrgFile=${tasks.reobfJar.srg.get().asFile}", 132 | "-AoutSrgFile=${mixinSrg}", 133 | "-AoutRefMapFile=${refMap}", 134 | ] 135 | } 136 | } 137 | 138 | if (project.use_access_transformer.toBoolean()) { 139 | for (File at : sourceSets.getByName("main").resources.files) { 140 | if (at.name.toLowerCase().endsWith("_at.cfg")) { 141 | tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(at) 142 | tasks.srgifyBinpatchedJar.accessTransformerFiles.from(at) 143 | } 144 | } 145 | } 146 | 147 | processResources { 148 | // this will ensure that this task is redone when the versions change. 149 | inputs.property 'version', project.version 150 | inputs.property 'mcversion', project.minecraft.version 151 | // replace stuff in mcmod.info, nothing else 152 | filesMatching(['mcmod.info', 'pack.mcmeta']) { fcd -> 153 | // replace version and mcversion 154 | fcd.expand( 155 | 'version': project.version, 156 | 'mcversion': project.minecraft.version 157 | ) 158 | } 159 | 160 | if (project.use_access_transformer.toBoolean()) { 161 | rename '(.+_at.cfg)', 'META-INF/$1' // Access Transformers 162 | } 163 | 164 | if (project.use_mixins.toBoolean()) { 165 | // Embed mixin refmap 166 | from refMap 167 | dependsOn("compileJava") 168 | } 169 | } 170 | 171 | jar { 172 | manifest { 173 | def attribute_map = [:] 174 | if (project.use_coremod.toBoolean()) { 175 | attribute_map['FMLCorePlugin'] = project.coremod_plugin_class_name 176 | if (project.include_mod.toBoolean()) { 177 | attribute_map['FMLCorePluginContainsFMLMod'] = true 178 | attribute_map['ForceLoadAsMod'] = project.gradle.startParameter.taskNames[0] == "build" 179 | } 180 | } 181 | if (project.use_access_transformer.toBoolean()) { 182 | attribute_map['FMLAT'] = project.archives_base_name + '_at.cfg' 183 | } 184 | attributes(attribute_map) 185 | } 186 | // Add all embedded dependencies into the jar 187 | setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) 188 | from(provider { 189 | configurations.embed.collect { 190 | if (it.isDirectory()) { 191 | return it 192 | } else { 193 | zipTree(it).matching { 194 | exclude '**/LICENSE*', 'META-INF/**' 195 | } 196 | } 197 | } 198 | }) 199 | } 200 | 201 | idea { 202 | module { inheritOutputDirs = true } 203 | project { 204 | settings { 205 | runConfigurations { 206 | "1. Run Client"(Gradle) { 207 | taskNames = ["runClient"] 208 | } 209 | "2. Run Server"(Gradle) { 210 | taskNames = ["runServer"] 211 | } 212 | "3. Run Obfuscated Client"(Gradle) { 213 | taskNames = ["runObfClient"] 214 | } 215 | "4. Run Obfuscated Server"(Gradle) { 216 | taskNames = ["runObfServer"] 217 | } 218 | } 219 | compiler.javac { 220 | afterEvaluate { 221 | javacAdditionalOptions = "-encoding utf8" 222 | moduleJavacAdditionalOptions = [ 223 | (project.name + ".main"): tasks.compileJava.options.compilerArgs.collect { '"' + it + '"' }.join(' ') 224 | ] 225 | } 226 | } 227 | } 228 | } 229 | } 230 | 231 | tasks.named("processIdeaSettings").configure { 232 | dependsOn("injectTags") 233 | } 234 | 235 | sourceSets { 236 | generator { 237 | java { 238 | srcDir 'src/generator/java' 239 | } 240 | } 241 | api { 242 | java { 243 | srcDir 'src/api/java' 244 | } 245 | } 246 | } 247 | 248 | tasks.register('generateParameterNameMappings', JavaExec) { 249 | mainClass = 'youyihj.probezs.generator.MethodParameterNamesGenerator' 250 | classpath = sourceSets.generator.runtimeClasspath 251 | 252 | // Configuration specific to the code generator 253 | args 'ModSources' 254 | args 'generated' 255 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Sets default memory used for gradle commands. Can be overridden by user or command line properties. 2 | # This is required to provide enough memory for the Minecraft decompilation process. 3 | org.gradle.jvmargs = -Xmx3G 4 | 5 | # Mod Information 6 | version = 1.18.7 7 | maven_group = youyihj.probezs 8 | archives_base_name = ProbeZS 9 | 10 | # If any properties changes below this line, run `gradlew setupDecompWorkspace` and refresh gradle again to ensure everything is working correctly. 11 | 12 | # Boilerplate Options 13 | use_mixins=true 14 | use_coremod = true 15 | use_assetmover = false 16 | 17 | # Access Transformer files should be in the root of `resources` folder and with the filename formatted as: `{archives_base_name}_at.cfg` 18 | use_access_transformer = true 19 | 20 | # Coremod Arguments 21 | include_mod = true 22 | coremod_plugin_class_name = youyihj.probezs.core.CoreMod -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendlyhj/ProbeZS/3223eeba383493fc4309c96e6b1d83caefac1545/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 134 | 135 | Please set the JAVA_HOME variable in your environment to match the 136 | location of your Java installation." 137 | fi 138 | 139 | # Increase the maximum file descriptors if we can. 140 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 141 | case $MAX_FD in #( 142 | max*) 143 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 144 | # shellcheck disable=SC3045 145 | MAX_FD=$( ulimit -H -n ) || 146 | warn "Could not query maximum file descriptor limit" 147 | esac 148 | case $MAX_FD in #( 149 | '' | soft) :;; #( 150 | *) 151 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 152 | # shellcheck disable=SC3045 153 | ulimit -n "$MAX_FD" || 154 | warn "Could not set maximum file descriptor limit to $MAX_FD" 155 | esac 156 | fi 157 | 158 | # Collect all arguments for the java command, stacking in reverse order: 159 | # * args from the command line 160 | # * the main class name 161 | # * -classpath 162 | # * -D...appname settings 163 | # * --module-path (only if needed) 164 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 165 | 166 | # For Cygwin or MSYS, switch paths to Windows format before running java 167 | if "$cygwin" || "$msys" ; then 168 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 169 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 170 | 171 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 172 | 173 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 174 | for arg do 175 | if 176 | case $arg in #( 177 | -*) false ;; # don't mess with options #( 178 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 179 | [ -e "$t" ] ;; #( 180 | *) false ;; 181 | esac 182 | then 183 | arg=$( cygpath --path --ignore --mixed "$arg" ) 184 | fi 185 | # Roll the args list around exactly as many times as the number of 186 | # args, so each arg winds up back in the position where it started, but 187 | # possibly modified. 188 | # 189 | # NB: a `for` loop captures its iteration list before it begins, so 190 | # changing the positional parameters here affects neither the number of 191 | # iterations, nor the values presented in `arg`. 192 | shift # remove old arg 193 | set -- "$@" "$arg" # push replacement arg 194 | done 195 | fi 196 | 197 | 198 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 199 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 200 | 201 | # Collect all arguments for the java command; 202 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 203 | # shell script including quotes and variable substitutions, so put them in 204 | # double quotes to make sure that they get re-expanded; and 205 | # * put everything else in single quotes, so that it's not re-expanded. 206 | 207 | set -- \ 208 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 209 | -classpath "$CLASSPATH" \ 210 | org.gradle.wrapper.GradleWrapperMain \ 211 | "$@" 212 | 213 | # Stop when "xargs" is not available. 214 | if ! command -v xargs >/dev/null 2>&1 215 | then 216 | die "xargs is not available" 217 | fi 218 | 219 | # Use "xargs" to parse quoted args. 220 | # 221 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 222 | # 223 | # In Bash we could simply go: 224 | # 225 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 226 | # set -- "${ARGS[@]}" "$@" 227 | # 228 | # but POSIX shell has neither arrays nor command substitution, so instead we 229 | # post-process each arg (as a line of input to sed) to backslash-escape any 230 | # character that might be a shell metacharacter, then use eval to reverse 231 | # that process (while maintaining the separation between arguments), and wrap 232 | # the whole thing up as a single "set" statement. 233 | # 234 | # This will of course break if any of these variables contains a newline or 235 | # an unmatched quote. 236 | # 237 | 238 | eval "set -- $( 239 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 240 | xargs -n1 | 241 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 242 | tr '\n' ' ' 243 | )" '"$@"' 244 | 245 | exec "$JAVACMD" "$@" 246 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /logs/latest.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/friendlyhj/ProbeZS/3223eeba383493fc4309c96e6b1d83caefac1545/logs/latest.log -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | // RetroFuturaGradle 5 | name = "GTNH Maven" 6 | url = uri("http://jenkins.usrv.eu:8081/nexus/content/groups/public/") 7 | allowInsecureProtocol = true 8 | mavenContent { 9 | includeGroup("com.gtnewhorizons") 10 | includeGroup("com.gtnewhorizons.retrofuturagradle") 11 | } 12 | } 13 | gradlePluginPortal() 14 | mavenCentral() 15 | mavenLocal() 16 | } 17 | } 18 | 19 | plugins { 20 | // Automatic toolchain provisioning 21 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0" 22 | } 23 | 24 | rootProject.name = archives_base_name 25 | 26 | -------------------------------------------------------------------------------- /src/generator/java/youyihj/probezs/generator/MethodParameterNamesGenerator.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.generator; 2 | 3 | import org.apache.commons.compress.utils.Sets; 4 | import org.yaml.snakeyaml.DumperOptions; 5 | import org.yaml.snakeyaml.Yaml; 6 | import spoon.Launcher; 7 | import spoon.reflect.CtModel; 8 | import spoon.reflect.declaration.*; 9 | import spoon.reflect.visitor.Filter; 10 | import spoon.reflect.visitor.filter.AnnotationFilter; 11 | import spoon.reflect.visitor.filter.CompositeFilter; 12 | import spoon.reflect.visitor.filter.FilteringOperator; 13 | import spoon.reflect.visitor.filter.TypeFilter; 14 | import stanhebben.zenscript.annotations.*; 15 | 16 | import java.io.File; 17 | import java.io.FileWriter; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.nio.file.Path; 21 | import java.nio.file.Paths; 22 | import java.util.*; 23 | import java.util.stream.Collectors; 24 | 25 | 26 | public class MethodParameterNamesGenerator { 27 | 28 | private static final Set extras = Sets.newHashSet( 29 | "crafttweaker.runtime.GlobalFunctions::print", 30 | "crafttweaker.runtime.GlobalFunctions::totalActions", 31 | "crafttweaker.runtime.GlobalFunctions::enableDebug", 32 | "crafttweaker.runtime.GlobalFunctions::isNull" 33 | ); 34 | 35 | private static final Set invalidUnits = Sets.newHashSet( 36 | Paths.get("gregtech", "api", "util", "RelativeDirection.java") 37 | ); 38 | 39 | 40 | public static void main(String[] args) { 41 | String folderPath = args[0]; 42 | String outputPath = args[1]; 43 | 44 | Launcher launcher = new Launcher(); 45 | launcher.addInputResource(folderPath); 46 | launcher.getModelBuilder().addCompilationUnitFilter(MethodParameterNamesGenerator::isInvalidUnit); 47 | launcher.buildModel(); 48 | CtModel model = launcher.getModel(); 49 | 50 | Filter> filter = new CompositeFilter<>( 51 | FilteringOperator.INTERSECTION, 52 | new TypeFilter<>(CtExecutable.class), 53 | new CompositeFilter<>( 54 | FilteringOperator.UNION, 55 | new AnnotationFilter<>(ZenMethod.class), 56 | new AnnotationFilter<>(ZenMethodStatic.class), 57 | new AnnotationFilter<>(ZenCaster.class), 58 | new AnnotationFilter<>(ZenOperator.class), 59 | new AnnotationFilter<>(ZenConstructor.class) 60 | ) 61 | ); 62 | List> methods = new ArrayList<>(model.getElements(filter)); 63 | 64 | // add globals 65 | addExtras(model, methods); 66 | Map>> result = generateSource(methods); 67 | addDefaults(result); 68 | DumperOptions options = new DumperOptions(); 69 | options.setAllowReadOnlyProperties(true); 70 | options.setPrettyFlow(true); 71 | options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); 72 | Yaml yaml = new Yaml(options); 73 | 74 | try { 75 | File file = new File(outputPath + "/method-parameter-names.yaml"); 76 | file.getParentFile().mkdirs(); 77 | yaml.dump(result, new FileWriter(file)); 78 | } catch (IOException e) { 79 | throw new RuntimeException(e); 80 | } 81 | } 82 | 83 | private static void addDefaults(Map>> result) { 84 | try (InputStream inputStream = MethodParameterNamesGenerator.class.getClassLoader().getResourceAsStream("default.yaml")) { 85 | Yaml yaml = new Yaml(); 86 | Map>> map = yaml.loadAs(inputStream, Map.class); 87 | result.putAll(map); 88 | } catch (IOException e) { 89 | throw new RuntimeException(e); 90 | } 91 | } 92 | 93 | 94 | private static void addExtras(CtModel model, List> methods) { 95 | // functional interface 96 | model.getElements(new CompositeFilter<>( 97 | FilteringOperator.INTERSECTION, 98 | new TypeFilter<>(CtInterface.class), 99 | new AnnotationFilter<>(ZenClass.class) 100 | )).stream().map( 101 | it -> { 102 | Set> ele = it.getAllMethods(); 103 | return ele.stream() 104 | .filter(m -> !m.isDefaultMethod()) 105 | .filter(CtModifiable::isPublic) 106 | .filter(CtModifiable::isAbstract) 107 | .collect(Collectors.toList()); 108 | } 109 | ) 110 | .filter(it -> it.size() == 1) 111 | .map(it -> it.get(0)) 112 | .forEach(methods::add); 113 | 114 | // manuals 115 | model.getElements(new TypeFilter<>(CtMethod.class)) 116 | .stream().filter( 117 | it -> { 118 | CtType declaringType = it.getDeclaringType(); 119 | if (declaringType == null) { 120 | return false; 121 | } 122 | String fullName = declaringType.getQualifiedName() + "::" + it.getSimpleName(); 123 | return extras.contains(fullName); 124 | } 125 | ).forEach(methods::add); 126 | } 127 | 128 | 129 | private static Map>> generateSource(List> methods) { 130 | Map>> result = new HashMap<>(); 131 | // Process each method 132 | for (CtExecutable method : methods) { 133 | String clazzName = ((CtTypeMember) method).getDeclaringType().getQualifiedName(); 134 | String methodName = method.getSimpleName(); 135 | List parameterNames = new ArrayList<>(); 136 | List parameterSignatures = new ArrayList<>(); 137 | 138 | 139 | if (method.getParameters().isEmpty()) { 140 | continue; 141 | } 142 | for (CtParameter parameter : method.getParameters()) { 143 | parameterNames.add(parameter.getSimpleName()); 144 | parameterSignatures.add(parameter.getType().getQualifiedName()); 145 | } 146 | 147 | Map data = new LinkedHashMap<>(); 148 | data.put("name", methodName); 149 | data.put("paramsSignature", String.join(",", parameterSignatures)); 150 | data.put("paramNames", parameterNames); 151 | 152 | result.computeIfAbsent(clazzName, it -> new ArrayList<>()) 153 | .add(data); 154 | 155 | } 156 | 157 | return result; 158 | } 159 | 160 | private static boolean isInvalidUnit(String unitPath) { 161 | for (Path invalidClass : invalidUnits) { 162 | if (Paths.get(unitPath).endsWith(invalidClass)) { 163 | return true; 164 | } 165 | } 166 | return false; 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/generator/resources/default.yaml: -------------------------------------------------------------------------------- 1 | java.lang.Math: 2 | - name: min 3 | paramNames: 4 | - a 5 | - b 6 | paramsSignature: int,int 7 | - name: max 8 | paramNames: 9 | - a 10 | - b 11 | paramsSignature: int,int 12 | - name: pow 13 | paramNames: 14 | - a 15 | - b 16 | paramsSignature: double,double 17 | java.lang.String: 18 | - name: equals 19 | paramsSignature: java.lang.Object 20 | paramNames: 21 | - anotherString 22 | - name: getChars 23 | paramsSignature: int,int,char[],int 24 | paramNames: 25 | - srcBegin 26 | - srcEnd 27 | - dst 28 | - dstBegin 29 | - name: compareTo 30 | paramsSignature: java.lang.String 31 | paramNames: 32 | - anotherString 33 | - name: indexOf 34 | paramsSignature: int 35 | paramNames: 36 | - ch 37 | - name: indexOf 38 | paramsSignature: java.lang.String 39 | paramNames: 40 | - str 41 | - name: indexOf 42 | paramsSignature: java.lang.String,int 43 | paramNames: 44 | - str 45 | - fromIndex 46 | - name: indexOf 47 | paramsSignature: int,int 48 | paramNames: 49 | - ch 50 | - fromIndex 51 | - name: charAt 52 | paramsSignature: int 53 | paramNames: 54 | - index 55 | - name: codePointAt 56 | paramsSignature: int 57 | paramNames: 58 | - index 59 | - name: codePointBefore 60 | paramsSignature: int 61 | paramNames: 62 | - index 63 | - name: codePointCount 64 | paramsSignature: int,int 65 | paramNames: 66 | - beginIndex 67 | - endIndex 68 | - name: offsetByCodePoints 69 | paramsSignature: int,int 70 | paramNames: 71 | - index 72 | - codePointOffset 73 | - name: regionMatches 74 | paramsSignature: boolean,int,java.lang.String,int,int 75 | paramNames: 76 | - ignoreCase 77 | - toffset 78 | - other 79 | - ooffset 80 | - len 81 | - name: regionMatches 82 | paramsSignature: int,java.lang.String,int,int 83 | paramNames: 84 | - toffset 85 | - other 86 | - ooffset 87 | - len 88 | - name: startsWith 89 | paramsSignature: java.lang.String,int 90 | paramNames: 91 | - prefix 92 | - toffset 93 | - name: startsWith 94 | paramsSignature: java.lang.String 95 | paramNames: 96 | - prefix 97 | - name: lastIndexOf 98 | paramsSignature: java.lang.String 99 | paramNames: 100 | - str 101 | - name: lastIndexOf 102 | paramsSignature: java.lang.String,int 103 | paramNames: 104 | - str 105 | - fromIndex 106 | - name: lastIndexOf 107 | paramsSignature: int,int 108 | paramNames: 109 | - ch 110 | - fromIndex 111 | - name: lastIndexOf 112 | paramsSignature: int 113 | paramNames: 114 | - ch 115 | - name: substring 116 | paramsSignature: int,int 117 | paramNames: 118 | - beginIndex 119 | - endIndex 120 | - name: substring 121 | paramsSignature: int 122 | paramNames: 123 | - beginIndex 124 | - name: replace 125 | paramsSignature: char,char 126 | paramNames: 127 | - oldChar 128 | - newChar 129 | - name: replace 130 | paramsSignature: java.lang.CharSequence,java.lang.CharSequence 131 | paramNames: 132 | - target 133 | - replacement 134 | - name: matches 135 | paramsSignature: java.lang.String 136 | paramNames: 137 | - regex 138 | - name: replaceFirst 139 | paramsSignature: java.lang.String,java.lang.String 140 | paramNames: 141 | - regex 142 | - replacement 143 | - name: replaceAll 144 | paramsSignature: java.lang.String,java.lang.String 145 | paramNames: 146 | - regex 147 | - replacement 148 | - name: split 149 | paramsSignature: java.lang.String 150 | paramNames: 151 | - regex 152 | - name: split 153 | paramsSignature: java.lang.String,int 154 | paramNames: 155 | - regex 156 | - limit 157 | - name: repeat 158 | paramsSignature: int 159 | paramNames: 160 | - count 161 | - name: equalsIgnoreCase 162 | paramsSignature: java.lang.String 163 | paramNames: 164 | - anotherString 165 | - name: compareToIgnoreCase 166 | paramsSignature: java.lang.String 167 | paramNames: 168 | - anotherString 169 | - name: endsWith 170 | paramsSignature: java.lang.String 171 | paramNames: 172 | - suffix 173 | - name: subSequence 174 | paramsSignature: int,int 175 | paramNames: 176 | - beginIndex 177 | - endIndex 178 | - name: concat 179 | paramsSignature: java.lang.String 180 | paramNames: 181 | - str 182 | - name: contains 183 | paramsSignature: java.lang.CharSequence 184 | paramNames: 185 | - s 186 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/Environment.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.reflect.TypeToken; 6 | import youyihj.probezs.util.FileUtils; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.Path; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.TreeMap; 13 | 14 | /** 15 | * @author youyihj 16 | */ 17 | public class Environment { 18 | private static final Map ENV_MAP = new TreeMap<>(); 19 | private static final Gson GSON = new GsonBuilder() 20 | .disableHtmlEscaping() 21 | .setPrettyPrinting() 22 | .create(); 23 | 24 | public static void put(String key, String value) { 25 | ENV_MAP.put(key, value); 26 | } 27 | 28 | public static void put(String key, List value) { 29 | ENV_MAP.put(key, value); 30 | } 31 | 32 | public static void output(Path path) { 33 | 34 | try { 35 | FileUtils.createFile(path, GSON.toJson(ENV_MAP, new TypeToken>() {}.getType())); 36 | } catch (IOException e) { 37 | ProbeZS.logger.error("Failed to dump env", e); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/ProbeZSConfig.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs; 2 | 3 | import net.minecraftforge.common.config.Config; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | @Config(modid = ProbeZS.MODID) 9 | public class ProbeZSConfig { 10 | 11 | @Config.Comment("whether output dzs files") 12 | public static boolean dumpDZS = true; 13 | 14 | @Config.Comment("whether output json files") 15 | public static boolean dumpJson = false; 16 | 17 | @Config.Comment("Communication port with language server") 18 | public static int socketPort = 6489; 19 | 20 | @Config.Comment("The way to collect zenscript libs") 21 | public static MemberCollector memberCollector = MemberCollector.REFLECTION; 22 | 23 | @Config.Comment("If true, outputs the source of expansion members in dzs") 24 | public static boolean outputSourceExpansionMembers = false; 25 | 26 | public enum MemberCollector { 27 | REFLECTION, 28 | ASM; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/api/BracketHandlerService.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.api; 2 | 3 | import javax.annotation.Nonnull; 4 | import javax.annotation.Nullable; 5 | import java.rmi.Remote; 6 | import java.rmi.RemoteException; 7 | 8 | /** 9 | * @author youyihj 10 | */ 11 | public interface BracketHandlerService extends Remote { 12 | @Nullable 13 | String getLocalizedName(String expr) throws RemoteException; 14 | 15 | @Nullable 16 | String getIcon(String expr) throws RemoteException; 17 | 18 | @Nonnull 19 | String getTypeName(String expr) throws RemoteException; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/api/BracketHandlerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.api; 2 | 3 | import youyihj.probezs.bracket.BracketHandlerCaller; 4 | 5 | import javax.annotation.Nonnull; 6 | import javax.annotation.Nullable; 7 | import java.io.Serializable; 8 | import java.rmi.Remote; 9 | 10 | /** 11 | * @author youyihj 12 | */ 13 | public class BracketHandlerServiceImpl implements BracketHandlerService { 14 | @Nullable 15 | @Override 16 | public String getLocalizedName(String expr) { 17 | return BracketHandlerCaller.INSTANCE.getLocalizedName(expr); 18 | } 19 | 20 | @Nullable 21 | @Override 22 | public String getIcon(String expr) { 23 | return BracketHandlerCaller.INSTANCE.getIcon(expr); 24 | } 25 | 26 | @Nonnull 27 | @Override 28 | public String getTypeName(String expr) { 29 | return BracketHandlerCaller.INSTANCE.getTypeName(expr); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/bracket/BracketHandlerCaller.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.bracket; 2 | 3 | import crafttweaker.api.item.IItemStack; 4 | import crafttweaker.api.liquid.ILiquidStack; 5 | import crafttweaker.api.minecraft.CraftTweakerMC; 6 | import crafttweaker.api.oredict.IOreDictEntry; 7 | import crafttweaker.zenscript.GlobalRegistry; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.renderer.GlStateManager; 10 | import net.minecraftforge.oredict.OreDictionary; 11 | import org.lwjgl.opengl.GL11; 12 | import org.objectweb.asm.*; 13 | import stanhebben.zenscript.ZenTokener; 14 | import stanhebben.zenscript.compiler.EnvironmentClass; 15 | import stanhebben.zenscript.compiler.EnvironmentMethod; 16 | import stanhebben.zenscript.compiler.IEnvironmentGlobal; 17 | import stanhebben.zenscript.compiler.ZenClassWriter; 18 | import stanhebben.zenscript.expression.*; 19 | import stanhebben.zenscript.expression.partial.IPartialExpression; 20 | import stanhebben.zenscript.parser.Token; 21 | import stanhebben.zenscript.symbols.IZenSymbol; 22 | import stanhebben.zenscript.type.ZenType; 23 | import stanhebben.zenscript.type.natives.JavaMethod; 24 | import stanhebben.zenscript.util.MethodOutput; 25 | import stanhebben.zenscript.util.ZenPosition; 26 | import youyihj.probezs.ProbeZS; 27 | import youyihj.probezs.api.BracketHandlerService; 28 | import youyihj.probezs.core.BytecodeClassLoader; 29 | import youyihj.probezs.render.RenderHelper; 30 | import youyihj.probezs.render.RenderTaskDispatcher; 31 | 32 | import javax.annotation.Nonnull; 33 | import javax.annotation.Nullable; 34 | import javax.imageio.ImageIO; 35 | import java.awt.image.BufferedImage; 36 | import java.io.ByteArrayOutputStream; 37 | import java.io.IOException; 38 | import java.lang.reflect.Field; 39 | import java.lang.reflect.Method; 40 | import java.util.*; 41 | import java.util.concurrent.TimeUnit; 42 | import java.util.function.Supplier; 43 | 44 | import static org.objectweb.asm.Opcodes.*; 45 | 46 | /** 47 | * @author youyihj 48 | */ 49 | public class BracketHandlerCaller implements BracketHandlerService { 50 | public static final BracketHandlerCaller INSTANCE = new BracketHandlerCaller(); 51 | public static final BracketHandlerResult EMPTY_RESULT = new BracketHandlerResult(null, ZenType.ANY); 52 | 53 | private static final IEnvironmentGlobal ENVIRONMENT_GLOBAL = GlobalRegistry.makeGlobalEnvironment(new HashMap<>()); 54 | private static final ZenPosition POSITION = new ZenPosition(null, 1, 0, "BracketHandlerCaller.java"); 55 | private static final Map> CACHED_SUPPLIER_CLASSES = new HashMap<>(); 56 | private static final Field CALL_STATIC_METHOD_FIELD; 57 | private static final Field CALL_STATIC_ARGUMENTS_FILED; 58 | private static final Field EXPRESSION_INT_VALUE; 59 | private static final Field EXPRESSION_FLOAT_VALUE; 60 | private static final Field EXPRESSION_STRING_VALUE; 61 | private static final BytecodeClassLoader CLASS_LOADER = new BytecodeClassLoader(ProbeZS.class.getClassLoader()); 62 | 63 | static { 64 | try { 65 | CALL_STATIC_METHOD_FIELD = ExpressionCallStatic.class.getDeclaredField("method"); 66 | CALL_STATIC_METHOD_FIELD.setAccessible(true); 67 | CALL_STATIC_ARGUMENTS_FILED = ExpressionCallStatic.class.getDeclaredField("arguments"); 68 | CALL_STATIC_ARGUMENTS_FILED.setAccessible(true); 69 | EXPRESSION_INT_VALUE = ExpressionInt.class.getDeclaredField("value"); 70 | EXPRESSION_INT_VALUE.setAccessible(true); 71 | EXPRESSION_FLOAT_VALUE = ExpressionFloat.class.getDeclaredField("value"); 72 | EXPRESSION_FLOAT_VALUE.setAccessible(true); 73 | EXPRESSION_STRING_VALUE = ExpressionString.class.getDeclaredField("value"); 74 | EXPRESSION_STRING_VALUE.setAccessible(true); 75 | } catch (NoSuchFieldException e) { 76 | throw new RuntimeException(e); 77 | } 78 | } 79 | 80 | public BracketHandlerResult query(String content) { 81 | String className = "bh$" + content.replaceAll("\\W", "_"); 82 | IPartialExpression expression = getZenExpression(content); 83 | BracketHandlerResult result; 84 | if (expression == null) { 85 | return EMPTY_RESULT; 86 | } 87 | if (expression instanceof ExpressionCallStatic) { 88 | result = getCached(((ExpressionCallStatic) expression), className); 89 | } else { 90 | result = getDirectly(expression, className); 91 | } 92 | if (result.getObject() == null) { 93 | return EMPTY_RESULT; 94 | } 95 | return result; 96 | } 97 | 98 | private static IPartialExpression getZenExpression(String content) { 99 | try { 100 | ZenTokener tokener = new ZenTokener(content, GlobalRegistry.getEnvironment(), "", false); 101 | List tokens = new ArrayList<>(); 102 | while (tokener.hasNext()) { 103 | tokens.add(tokener.next()); 104 | } 105 | IZenSymbol symbol = GlobalRegistry.resolveBracket(ENVIRONMENT_GLOBAL, tokens); 106 | if (symbol == null) { 107 | return null; 108 | } 109 | return symbol.instance(POSITION); 110 | } catch (IOException e) { 111 | throw new AssertionError(); 112 | } 113 | } 114 | 115 | private static BracketHandlerResult getCached(ExpressionCallStatic expressionCallStatic, String className) { 116 | Method method = ((JavaMethod) getFieldUnchecked(CALL_STATIC_METHOD_FIELD, expressionCallStatic)).getMethod(); 117 | Expression[] expressions = getFieldUnchecked(CALL_STATIC_ARGUMENTS_FILED, expressionCallStatic); 118 | Class clazz = CACHED_SUPPLIER_CLASSES.computeIfAbsent(method, (method1 -> { 119 | byte[] bytecode = defineArgumentSupplierClass(method1, expressions, className); 120 | CLASS_LOADER.putBytecode(className, bytecode); 121 | // try { 122 | // FileUtils.writeByteArrayToFile(new File("bhClasses/" + className + ".class"), bytecode); 123 | // } catch (IOException ex) { 124 | // throw new RuntimeException(ex); 125 | // } 126 | try { 127 | return Class.forName(className, true, CLASS_LOADER); 128 | } catch (ClassNotFoundException e) { 129 | throw new AssertionError(); 130 | } 131 | })); 132 | Object[] argumentsForConstructor = new Object[expressions.length]; 133 | Class[] parameterTypes = method.getParameterTypes(); 134 | for (int i = 0; i < expressions.length; i++) { 135 | Expression expression = expressions[i]; 136 | Class parameterType = parameterTypes[i]; 137 | if (expression instanceof ExpressionInt) { 138 | long value = 0; 139 | try { 140 | value = EXPRESSION_INT_VALUE.getLong(expression); 141 | } catch (IllegalAccessException e) { 142 | throw new RuntimeException(e); 143 | } 144 | if (parameterType == int.class) { 145 | argumentsForConstructor[i] = ((int) value); 146 | } else if (parameterType == long.class) { 147 | argumentsForConstructor[i] = value; 148 | } else if (parameterType == short.class) { 149 | argumentsForConstructor[i] = ((short) value); 150 | } else if (parameterType == byte.class) { 151 | argumentsForConstructor[i] = ((byte) value); 152 | } else { 153 | return getDirectly(expressionCallStatic, className); 154 | } 155 | } else if (expression instanceof ExpressionFloat) { 156 | double value = 0; 157 | try { 158 | value = EXPRESSION_FLOAT_VALUE.getDouble(expression); 159 | } catch (IllegalAccessException e) { 160 | throw new RuntimeException(e); 161 | } 162 | if (parameterType == double.class) { 163 | argumentsForConstructor[i] = value; 164 | } else if (parameterType == float.class) { 165 | argumentsForConstructor[i] = ((float) value); 166 | } else { 167 | return getDirectly(expressionCallStatic, className); 168 | } 169 | } else if (expression instanceof ExpressionString) { 170 | String value = getFieldUnchecked(EXPRESSION_STRING_VALUE, expression); 171 | if (parameterType == String.class) { 172 | argumentsForConstructor[i] = value; 173 | } else { 174 | return getDirectly(expressionCallStatic, className); 175 | } 176 | } 177 | } 178 | try { 179 | Object obj = ((Supplier) clazz.getConstructor(parameterTypes).newInstance(argumentsForConstructor)).get(); 180 | return new BracketHandlerResult(obj, expressionCallStatic.getType()); 181 | } catch (ReflectiveOperationException e) { 182 | return getDirectly(expressionCallStatic, className); 183 | } 184 | } 185 | 186 | private static BracketHandlerResult getDirectly(IPartialExpression expression, String className) { 187 | Class supplierClass; 188 | try { 189 | supplierClass = Class.forName(className, true, CLASS_LOADER); 190 | } catch (ClassNotFoundException e) { 191 | byte[] bytecode = defineDirectSupplierClass(expression, className); 192 | CLASS_LOADER.putBytecode(className, bytecode); 193 | // try { 194 | // FileUtils.writeByteArrayToFile(new File("bhClasses/" + className + ".class"), bytecode); 195 | // } catch (IOException ex) { 196 | // throw new RuntimeException(ex); 197 | // } 198 | return getDirectly(expression, className); 199 | } 200 | try { 201 | return new BracketHandlerResult(((Supplier) supplierClass.newInstance()).get(), expression.getType()); 202 | } catch (InstantiationException | IllegalAccessException e) { 203 | throw new RuntimeException(e); 204 | } 205 | } 206 | 207 | private static byte[] defineArgumentSupplierClass(Method method, Expression[] expressions, String className) { 208 | ClassWriter classWriter = new ZenClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 209 | classWriter.visit(V1_8, ACC_PUBLIC, className, null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(Supplier.class)}); 210 | StringBuilder constructorDesc = new StringBuilder("("); 211 | for (int i = 0; i < expressions.length; i++) { 212 | Expression expression = expressions[i]; 213 | String typeDesc = expression.getType().toASMType().getDescriptor(); 214 | FieldVisitor fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "arg" + i, typeDesc, null, null); 215 | constructorDesc.append(typeDesc); 216 | fieldVisitor.visitEnd(); 217 | } 218 | constructorDesc.append(")V"); 219 | MethodVisitor constructor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "", constructorDesc.toString(), null, null); 220 | constructor.visitCode(); 221 | constructor.visitVarInsn(ALOAD, 0); 222 | constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); 223 | int paraIndex = 0; 224 | for (int i = 0; i < expressions.length; i++) { 225 | Expression expression = expressions[i]; 226 | Type asmType = expression.getType().toASMType(); 227 | constructor.visitVarInsn(ALOAD, 0); 228 | paraIndex += asmType.getSize(); 229 | constructor.visitVarInsn(asmType.getOpcode(ILOAD), paraIndex); 230 | constructor.visitFieldInsn(PUTFIELD, className, "arg" + i, asmType.getDescriptor()); 231 | } 232 | constructor.visitInsn(RETURN); 233 | constructor.visitMaxs(1, 1); 234 | constructor.visitEnd(); 235 | MethodVisitor getMethod = classWriter.visitMethod(ACC_PUBLIC, "get", "()" + Type.getDescriptor(Object.class), null, null); 236 | getMethod.visitCode(); 237 | for (int i = 0; i < expressions.length; i++) { 238 | Expression expression = expressions[i]; 239 | Type asmType = expression.getType().toASMType(); 240 | getMethod.visitVarInsn(ALOAD, 0); 241 | getMethod.visitFieldInsn(GETFIELD, className, "arg" + i, asmType.getDescriptor()); 242 | } 243 | getMethod.visitMethodInsn(INVOKESTATIC, Type.getInternalName(method.getDeclaringClass()), method.getName(), Type.getMethodDescriptor(method), false); 244 | getMethod.visitInsn(ARETURN); 245 | getMethod.visitMaxs(1, 1); 246 | getMethod.visitEnd(); 247 | classWriter.visitEnd(); 248 | return classWriter.toByteArray(); 249 | } 250 | 251 | private static byte[] defineDirectSupplierClass(IPartialExpression expression, String className) { 252 | ClassWriter classWriter = new ZenClassWriter(ClassWriter.COMPUTE_FRAMES); 253 | classWriter.visit(V1_8, ACC_PUBLIC, className, null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(Supplier.class)}); 254 | MethodVisitor constructor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); 255 | constructor.visitCode(); 256 | constructor.visitVarInsn(ALOAD, 0); 257 | constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false); 258 | constructor.visitInsn(RETURN); 259 | constructor.visitMaxs(1, 1); 260 | constructor.visitEnd(); 261 | MethodOutput methodOutput = new MethodOutput(classWriter, Opcodes.ACC_PUBLIC, "get", "()" + Type.getDescriptor(Object.class), null, null); 262 | EnvironmentClass environmentClass = new EnvironmentClass(classWriter, ENVIRONMENT_GLOBAL); 263 | EnvironmentMethod environmentMethod = new EnvironmentMethod(methodOutput, environmentClass); 264 | methodOutput.start(); 265 | expression.eval(environmentMethod).compile(true, environmentMethod); 266 | methodOutput.returnObject(); 267 | methodOutput.end(); 268 | classWriter.visitEnd(); 269 | return classWriter.toByteArray(); 270 | } 271 | 272 | @SuppressWarnings("unchecked") 273 | private static T getFieldUnchecked(Field field, Object obj) { 274 | try { 275 | return (T) field.get(obj); 276 | } catch (IllegalAccessException e) { 277 | throw new RuntimeException(e); 278 | } 279 | } 280 | 281 | @Nullable 282 | @Override 283 | public String getLocalizedName(String expr) { 284 | BracketHandlerResult result = query(expr); 285 | Object object = result.getObject(); 286 | if (object instanceof IItemStack) { 287 | return ProbeZS.safeGetItemName(((IItemStack) object)); 288 | } 289 | if (object instanceof ILiquidStack) { 290 | return ((ILiquidStack) object).getDisplayName(); 291 | } 292 | if (object instanceof IOreDictEntry) { 293 | return ProbeZS.safeGetItemName(((IOreDictEntry) object).getFirstItem()); 294 | } 295 | return null; 296 | } 297 | 298 | @Nullable 299 | @Override 300 | public String getIcon(String expr) { 301 | BracketHandlerResult result = query(expr); 302 | Object object = result.getObject(); 303 | if (object instanceof IItemStack) { 304 | try { 305 | return RenderTaskDispatcher.submit(() -> { 306 | IItemStack item = (IItemStack) object; 307 | if (item.getMetadata() == OreDictionary.WILDCARD_VALUE) { 308 | item = item.withDamage(0); 309 | } 310 | RenderHelper.setupRenderState(32); 311 | GlStateManager.pushMatrix(); 312 | GlStateManager.clearColor(0, 0, 0, 0); 313 | GlStateManager.clear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); 314 | Minecraft.getMinecraft().getRenderItem().renderItemAndEffectIntoGUI(CraftTweakerMC.getItemStack(item), 0, 0); 315 | GlStateManager.popMatrix(); 316 | RenderHelper.tearDownRenderState(); 317 | BufferedImage img = RenderHelper.createFlipped(RenderHelper.readPixels(32, 32)); 318 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 319 | try { 320 | ImageIO.write(img, "PNG", out); 321 | } catch (IOException ignored) { 322 | } 323 | return Base64.getEncoder().encodeToString(out.toByteArray()); 324 | }).get(3, TimeUnit.SECONDS); 325 | } catch (Exception e) { 326 | ProbeZS.logger.error(e); 327 | } 328 | } 329 | return null; 330 | } 331 | 332 | @Nonnull 333 | @Override 334 | public String getTypeName(String expr) { 335 | return query(expr).getZenType().getName(); 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/bracket/BracketHandlerEntry.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.bracket; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonSerializationContext; 6 | import com.google.gson.JsonSerializer; 7 | 8 | import java.lang.reflect.Type; 9 | 10 | /** 11 | * @author youyihj 12 | */ 13 | public class BracketHandlerEntry { 14 | private final String id; 15 | private final BracketHandlerEntryProperties properties; 16 | 17 | public BracketHandlerEntry(String id, BracketHandlerEntryProperties properties) { 18 | this.id = id; 19 | this.properties = properties; 20 | } 21 | 22 | public String getId() { 23 | return id; 24 | } 25 | 26 | public BracketHandlerEntryProperties getProperties() { 27 | return properties; 28 | } 29 | 30 | public static class Serializer implements JsonSerializer { 31 | 32 | @Override 33 | public JsonElement serialize(BracketHandlerEntry src, Type typeOfSrc, JsonSerializationContext context) { 34 | JsonObject json = new JsonObject(); 35 | json.addProperty("_id", src.getId()); 36 | src.getProperties().getProperties().forEach(json::add); 37 | return json; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/bracket/BracketHandlerEntryProperties.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.bracket; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonPrimitive; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * @author youyihj 13 | */ 14 | public class BracketHandlerEntryProperties { 15 | private final Map properties = new HashMap<>(); 16 | 17 | public void add(String key, JsonElement value, boolean meta) { 18 | if (meta) { 19 | key = "_" + key; 20 | } 21 | properties.put(key, value); 22 | } 23 | 24 | public void add(String key, String value, boolean meta) { 25 | add(key, new JsonPrimitive(value), meta); 26 | } 27 | 28 | public void add(String key, List value, boolean meta) { 29 | JsonArray array = new JsonArray(); 30 | value.forEach(it -> array.add(new JsonPrimitive(it))); 31 | add(key, array, meta); 32 | } 33 | 34 | public Map getProperties() { 35 | return properties; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/bracket/BracketHandlerMirror.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.bracket; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import youyihj.probezs.tree.JavaTypeMirror; 6 | import youyihj.probezs.tree.ZenClassTree; 7 | 8 | import java.lang.reflect.Type; 9 | import java.util.Collection; 10 | import java.util.List; 11 | import java.util.function.BiConsumer; 12 | import java.util.function.Function; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * @author youyihj 17 | */ 18 | public class BracketHandlerMirror { 19 | public static final Gson GSON = new GsonBuilder() 20 | .setPrettyPrinting() 21 | .disableHtmlEscaping() 22 | .registerTypeAdapter(JavaTypeMirror.class, new JavaTypeMirror.FullNameSerializer()) 23 | .registerTypeAdapter(BracketHandlerEntry.class, new BracketHandlerEntry.Serializer()) 24 | .create(); 25 | 26 | private final JavaTypeMirror type; 27 | private final String regex; 28 | private final List entries; 29 | 30 | public BracketHandlerMirror(JavaTypeMirror type, String regex, List entries) { 31 | this.type = type; 32 | this.regex = regex; 33 | this.entries = entries; 34 | } 35 | 36 | public JavaTypeMirror getType() { 37 | return type; 38 | } 39 | 40 | public String getRegex() { 41 | return regex; 42 | } 43 | 44 | public List getEntries() { 45 | return entries; 46 | } 47 | 48 | public static Builder builder(ZenClassTree classTree) { 49 | return new Builder<>(classTree); 50 | } 51 | 52 | public static class Builder { 53 | private final ZenClassTree classTree; 54 | private Type type; 55 | private String regex; 56 | private Collection entries; 57 | private Function idMapper; 58 | private BiConsumer propertiesAdder; 59 | 60 | Builder(ZenClassTree classTree) { 61 | this.classTree = classTree; 62 | } 63 | 64 | public Builder setType(Type type) { 65 | this.type = type; 66 | return this; 67 | } 68 | 69 | public Builder setRegex(String regex) { 70 | this.regex = regex; 71 | return this; 72 | } 73 | 74 | public Builder setEntries(Collection entries) { 75 | this.entries = entries; 76 | return this; 77 | } 78 | 79 | public Builder setIdMapper(Function idMapper) { 80 | this.idMapper = idMapper; 81 | return this; 82 | } 83 | 84 | public Builder setPropertiesAdder(BiConsumer propertiesAdder) { 85 | this.propertiesAdder = propertiesAdder; 86 | return this; 87 | } 88 | 89 | public BracketHandlerMirror build() { 90 | List entities = entries.stream() 91 | .map(it -> { 92 | BracketHandlerEntryProperties properties = new BracketHandlerEntryProperties(); 93 | if (propertiesAdder != null) { 94 | propertiesAdder.accept(it, properties); 95 | } 96 | return new BracketHandlerEntry(idMapper.apply(it), properties); 97 | }) 98 | .collect(Collectors.toList()); 99 | return new BracketHandlerMirror(classTree.createJavaTypeMirror(type), regex, entities); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/bracket/BracketHandlerResult.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.bracket; 2 | 3 | import stanhebben.zenscript.type.ZenType; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | public class BracketHandlerResult { 9 | private final Object object; 10 | private final ZenType zenType; 11 | 12 | public BracketHandlerResult(Object object, ZenType zenType) { 13 | this.object = object; 14 | this.zenType = zenType; 15 | } 16 | 17 | public Object getObject() { 18 | return object; 19 | } 20 | 21 | public ZenType getZenType() { 22 | return zenType; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/ASMMemberCollector.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core; 2 | 3 | import com.google.common.collect.Sets; 4 | import youyihj.probezs.ProbeZS; 5 | import youyihj.probezs.member.asm.ASMMemberFactory; 6 | 7 | /** 8 | * @author youyihj 9 | */ 10 | public class ASMMemberCollector { 11 | public static final ASMMemberFactory MEMBER_FACTORY = new ASMMemberFactory(Sets.newHashSet("ZenClass", "ZenExpansion"), () -> ASMMemberCollector.Holder::getMainLoader); 12 | 13 | public static class Holder { 14 | public static ClassLoader getMainLoader() { 15 | return ProbeZS.class.getClassLoader(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/BytecodeClassLoader.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class BytecodeClassLoader extends ClassLoader { 10 | private final Map> classes = new HashMap<>(); 11 | private final Map bytecodes = new HashMap<>(); 12 | 13 | public BytecodeClassLoader(ClassLoader parent) { 14 | super(parent); 15 | } 16 | 17 | @Override 18 | protected Class findClass(String name) throws ClassNotFoundException { 19 | if (classes.containsKey(name)) { 20 | return classes.get(name); 21 | } 22 | if (bytecodes.containsKey(name)) { 23 | byte[] bytes = bytecodes.get(name); 24 | Class clazz = defineClass(name, bytes, 0, bytes.length); 25 | classes.put(name, clazz); 26 | return clazz; 27 | } 28 | return super.findClass(name); 29 | } 30 | 31 | public void putBytecode(String name, byte[] bytecode) { 32 | bytecodes.put(name, bytecode); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/CoreMod.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core; 2 | 3 | import net.minecraft.launchwrapper.Launch; 4 | import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; 5 | import youyihj.probezs.Environment; 6 | import youyihj.probezs.ProbeZSConfig; 7 | import youyihj.probezs.util.DebugAPIAdapter; 8 | 9 | import javax.annotation.Nullable; 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.nio.charset.StandardCharsets; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.nio.file.Paths; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | /** 21 | * @author youyihj 22 | */ 23 | public class CoreMod implements IFMLLoadingPlugin { 24 | @Override 25 | public String[] getASMTransformerClass() { 26 | return new String[] {"youyihj.probezs.core.ProbeZSClassTransformer"}; 27 | } 28 | 29 | @Override 30 | public String getModContainerClass() { 31 | return null; 32 | } 33 | 34 | @Nullable 35 | @Override 36 | public String getSetupClass() { 37 | return null; 38 | } 39 | 40 | @Override 41 | public void injectData(Map data) { 42 | DebugAPIAdapter.init(); 43 | readMemberCollectorConfig((File) data.get("mcLocation")); 44 | Environment.put("launchArgs", getLaunchArguments()); 45 | } 46 | 47 | @Override 48 | public String getAccessTransformerClass() { 49 | return null; 50 | } 51 | 52 | private void readMemberCollectorConfig(File mcLocation) { 53 | Path configPath = Paths.get(mcLocation.toURI()).resolve("config/probezs.cfg"); 54 | try { 55 | for (String line : Files.readAllLines(configPath, StandardCharsets.UTF_8)) { 56 | String trimmedLine = line.trim(); 57 | String configKey = "S:memberCollector="; 58 | if (trimmedLine.startsWith(configKey)) { 59 | ProbeZSConfig.memberCollector = ProbeZSConfig.MemberCollector.valueOf(trimmedLine.substring(configKey.length())); 60 | } 61 | } 62 | } catch (IOException e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | 67 | @SuppressWarnings("unchecked") 68 | private List getLaunchArguments() { 69 | List args = new ArrayList<>(); 70 | Map forgeLaunchArgs = (Map) Launch.blackboard.get("forgeLaunchArgs"); 71 | forgeLaunchArgs.forEach((key, value) -> { 72 | // if (!"--accessToken".equals(key)) { 73 | args.add(key); 74 | args.add(value); 75 | // } 76 | }); 77 | return args; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/LateMixinInit.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core; 2 | 3 | import zone.rong.mixinbooter.ILateMixinLoader; 4 | 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | /** 9 | * @author youyihj 10 | */ 11 | public class LateMixinInit implements ILateMixinLoader { 12 | @Override 13 | public List getMixinConfigs() { 14 | return Collections.singletonList( 15 | "mixins.probezs.json" 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/ProbeZSClassTransformer.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core; 2 | 3 | import net.minecraft.launchwrapper.IClassTransformer; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.ClassWriter; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.tree.ClassNode; 8 | import youyihj.probezs.ProbeZSConfig; 9 | import youyihj.probezs.core.asm.ASMCraftTweakerAPI; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | /** 15 | * @author youyihj 16 | */ 17 | public class ProbeZSClassTransformer implements IClassTransformer { 18 | 19 | private static final List EXCLUDE_PACKAGES = Arrays.asList( 20 | "youyihj.probezs", 21 | "org.spongepowered", 22 | "net.minecraft" 23 | ); 24 | 25 | @Override 26 | public byte[] transform(String name, String transformedName, byte[] basicClass) { 27 | if (basicClass == null) 28 | return null; 29 | if ("crafttweaker.CraftTweakerAPI".equals(name)) { 30 | ClassWriter classWriter = new ClassWriter(0); 31 | ASMCraftTweakerAPI asm = new ASMCraftTweakerAPI(Opcodes.ASM5, classWriter); 32 | new ClassReader(basicClass).accept(asm, 0); 33 | return classWriter.toByteArray(); 34 | } 35 | for (String excludePackage : EXCLUDE_PACKAGES) { 36 | if (transformedName.startsWith(excludePackage)) { 37 | return basicClass; 38 | } 39 | } 40 | if (ProbeZSConfig.memberCollector == ProbeZSConfig.MemberCollector.ASM) { 41 | ClassNode classNode = new ClassNode(); 42 | new ClassReader(basicClass).accept(classNode, 0); 43 | ASMMemberCollector.MEMBER_FACTORY.putClassNode(classNode); 44 | } 45 | return basicClass; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/asm/ASMCraftTweakerAPI.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.asm; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | 7 | /** 8 | * @author youyihj 9 | */ 10 | public class ASMCraftTweakerAPI extends ClassVisitor { 11 | public ASMCraftTweakerAPI(int api, ClassVisitor cv) { 12 | super(api, cv); 13 | } 14 | 15 | @Override 16 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 17 | if (name.equals("registerClass")) { 18 | return new InsertHookMethodVisitor(api, super.visitMethod(access, name, desc, signature, exceptions)); 19 | } 20 | return super.visitMethod(access, name, desc, signature, exceptions); 21 | } 22 | 23 | private static class InsertHookMethodVisitor extends MethodVisitor implements Opcodes { 24 | 25 | public InsertHookMethodVisitor(int api, MethodVisitor mv) { 26 | super(api, mv); 27 | } 28 | 29 | @Override 30 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 31 | super.visitMethodInsn(opcode, owner, name, desc, itf); 32 | if ("getDeclaredMethods".equals(name)) { 33 | super.visitVarInsn(ALOAD, 0); 34 | super.visitMethodInsn(INVOKESTATIC, 35 | "youyihj/probezs/core/asm/CraftTweakerAPIHooks", 36 | "readClass", 37 | "(Ljava/lang/Class;)V", 38 | false); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/asm/CraftTweakerAPIHooks.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.asm; 2 | 3 | import net.minecraftforge.fml.common.Loader; 4 | import net.minecraftforge.fml.common.ModContainer; 5 | import youyihj.probezs.ProbeZS; 6 | import youyihj.probezs.ProbeZSConfig; 7 | import youyihj.probezs.tree.ZenClassTree; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * @author youyihj 13 | */ 14 | public class CraftTweakerAPIHooks { 15 | public static void readClass(Class clazz) { 16 | Map pathToModMap = ProbeZS.pathToModMap; 17 | if (ProbeZSConfig.outputSourceExpansionMembers && pathToModMap.isEmpty()) { 18 | Loader.instance().getActiveModList().forEach(mod -> { 19 | String uri = mod.getSource().toURI().toString(); 20 | if (!pathToModMap.containsKey(uri)) { 21 | pathToModMap.put(uri, mod); 22 | } 23 | }); 24 | } 25 | ZenClassTree.getRoot().putClass(clazz); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/hook/ILocalVariableMethodOutput.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.hook; 2 | 3 | import stanhebben.zenscript.definitions.zenclasses.ParsedZenClass; 4 | 5 | public interface ILocalVariableMethodOutput { 6 | 7 | void probeZS$enableLocalVariable(); 8 | 9 | void probeZS$injectThis(ParsedZenClass zenClass); 10 | 11 | void probeZS$beginScope(); 12 | 13 | void probeZS$endScope(); 14 | 15 | void probeZS$addVariable(LocalVariable variable); 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/hook/LocalVariable.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.hook; 2 | 3 | import org.objectweb.asm.Label; 4 | import org.objectweb.asm.Type; 5 | 6 | import java.util.function.IntSupplier; 7 | 8 | public class LocalVariable { 9 | 10 | private final String name; 11 | private final Type type; 12 | 13 | private final IntSupplier idxSupplier; 14 | 15 | private Label scopeBegin; 16 | private Label scopeEnd; 17 | 18 | public LocalVariable(String name, Type type, IntSupplier idxSupplier) { 19 | this.name = name; 20 | this.type = type; 21 | this.idxSupplier = idxSupplier; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public Type getType() { 29 | return type; 30 | } 31 | 32 | public IntSupplier getIdxSupplier() { 33 | return idxSupplier; 34 | } 35 | 36 | public Label getScopeBegin() { 37 | return scopeBegin; 38 | } 39 | 40 | public void setScopeBegin(Label scopeBegin) { 41 | this.scopeBegin = scopeBegin; 42 | } 43 | 44 | public Label getScopeEnd() { 45 | return scopeEnd; 46 | } 47 | 48 | public void setScopeEnd(Label scopeEnd) { 49 | this.scopeEnd = scopeEnd; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/hook/ParsedFunctionHooks.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.hook; 2 | 3 | import stanhebben.zenscript.definitions.ParsedFunction; 4 | 5 | public class ParsedFunctionHooks { 6 | 7 | public static ThreadLocal isGeneratingStatement = ThreadLocal.withInitial(() -> false); 8 | public static ThreadLocal currentFunction = new ThreadLocal<>(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/hook/ParsedZenClassHooks.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.hook; 2 | 3 | import stanhebben.zenscript.type.ZenTypeZenClass; 4 | 5 | public class ParsedZenClassHooks { 6 | public static final ThreadLocal current = new ThreadLocal<>(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/hook/Scope.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.hook; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Scope { 7 | private final List variables = new ArrayList<>(); 8 | 9 | public List getVariables() { 10 | return variables; 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/mixin/MixinEnvironmentMethod.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Final; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | import org.spongepowered.asm.mixin.Unique; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 11 | import stanhebben.zenscript.compiler.EnvironmentMethod; 12 | import stanhebben.zenscript.compiler.IEnvironmentClass; 13 | import stanhebben.zenscript.symbols.IZenSymbol; 14 | import stanhebben.zenscript.symbols.SymbolArgument; 15 | import stanhebben.zenscript.symbols.SymbolLocal; 16 | import stanhebben.zenscript.util.MethodOutput; 17 | import stanhebben.zenscript.util.ZenPosition; 18 | import youyihj.probezs.core.hook.ILocalVariableMethodOutput; 19 | import youyihj.probezs.core.hook.LocalVariable; 20 | import youyihj.probezs.core.hook.ParsedZenClassHooks; 21 | 22 | import java.util.HashMap; 23 | 24 | @Mixin(value = EnvironmentMethod.class, remap = false) 25 | public abstract class MixinEnvironmentMethod { 26 | 27 | @Shadow 28 | public abstract MethodOutput getOutput(); 29 | 30 | @Shadow 31 | @Final 32 | private HashMap locals; 33 | 34 | @Unique 35 | public int probeZS$getLocalDirect(SymbolLocal variable) { 36 | if (!locals.containsKey(variable)) { 37 | return -1; 38 | } 39 | return locals.get(variable); 40 | } 41 | 42 | @Inject(method = "", at = @At("RETURN")) 43 | private void inject_init(MethodOutput output, IEnvironmentClass environment, CallbackInfo ci) { 44 | ((ILocalVariableMethodOutput) output).probeZS$enableLocalVariable(); 45 | if (ParsedZenClassHooks.current.get() != null) { 46 | ((ILocalVariableMethodOutput) output).probeZS$injectThis(ParsedZenClassHooks.current.get().zenClass); 47 | } 48 | } 49 | 50 | @Inject(method = "putValue", 51 | at = @At("HEAD"), 52 | locals = LocalCapture.CAPTURE_FAILEXCEPTION 53 | ) 54 | private void inject_addVar(String name, IZenSymbol value, ZenPosition position, CallbackInfo ci) { 55 | ILocalVariableMethodOutput output = (ILocalVariableMethodOutput) this.getOutput(); 56 | 57 | if (value instanceof SymbolLocal) { 58 | SymbolLocal local = (SymbolLocal) value; 59 | LocalVariable localVariable = new LocalVariable(name, ((SymbolLocal) value).getType().toASMType(), () -> probeZS$getLocalDirect(local)); 60 | output.probeZS$addVariable(localVariable); 61 | } 62 | 63 | if (value instanceof SymbolArgument) { 64 | SymbolArgument args = (SymbolArgument) value; 65 | LocalVariable localVariable = new LocalVariable(name, ((SymbolArgumentAccessor) args).getType().toASMType(), () -> ((SymbolArgumentAccessor) args).getId()); 66 | output.probeZS$addVariable(localVariable); 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/mixin/MixinMethodOutput.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.mixin; 2 | 3 | import org.objectweb.asm.Label; 4 | import org.objectweb.asm.Type; 5 | import org.objectweb.asm.commons.LocalVariablesSorter; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.Unique; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 14 | import stanhebben.zenscript.definitions.zenclasses.ParsedZenClass; 15 | import stanhebben.zenscript.util.MethodOutput; 16 | import stanhebben.zenscript.util.ZenPosition; 17 | import youyihj.probezs.core.hook.ILocalVariableMethodOutput; 18 | import youyihj.probezs.core.hook.LocalVariable; 19 | import youyihj.probezs.core.hook.ParsedFunctionHooks; 20 | import youyihj.probezs.core.hook.Scope; 21 | 22 | import java.util.ArrayDeque; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | import java.util.Objects; 26 | 27 | @Mixin(value = MethodOutput.class, remap = false) 28 | public abstract class MixinMethodOutput implements ILocalVariableMethodOutput { 29 | 30 | @Shadow 31 | @Final 32 | private LocalVariablesSorter visitor; 33 | 34 | @Shadow 35 | public abstract void label(Label label); 36 | 37 | @Unique 38 | private Label probeZS$firstLabel; 39 | @Unique 40 | private Label probeZS$lastLabel; 41 | 42 | @Unique 43 | private final ArrayDeque probeZS$scopes = new ArrayDeque<>(); 44 | @Unique 45 | private final List probeZS$variables = new ArrayList<>(); 46 | @Unique 47 | private final List probeZS$unfinishedVariables = new ArrayList<>(); 48 | @Unique 49 | private boolean probeZS$enabled = false; 50 | 51 | 52 | @Unique 53 | public void probeZS$beginScope() { 54 | if (!probeZS$enabled) { 55 | return; 56 | } 57 | probeZS$scopes.push(new Scope()); 58 | } 59 | 60 | @Unique 61 | public void probeZS$endScope() { 62 | if (!probeZS$enabled) { 63 | return; 64 | } 65 | Scope scope = probeZS$scopes.pop(); 66 | for (LocalVariable variable : scope.getVariables()) { 67 | if (variable.getScopeBegin() == null) { 68 | variable.setScopeBegin(probeZS$firstLabel); 69 | } 70 | probeZS$unfinishedVariables.add(variable); 71 | } 72 | } 73 | 74 | @Unique 75 | private void probeZS$finishVariables() { 76 | if (!probeZS$enabled) { 77 | return; 78 | } 79 | for (LocalVariable unfinishedVariable : probeZS$unfinishedVariables) { 80 | unfinishedVariable.setScopeEnd(probeZS$lastLabel); 81 | probeZS$variables.add(unfinishedVariable); 82 | } 83 | probeZS$unfinishedVariables.clear(); 84 | } 85 | 86 | 87 | @Override 88 | public void probeZS$enableLocalVariable() { 89 | this.probeZS$scopes.push(new Scope()); 90 | this.probeZS$enabled = true; 91 | } 92 | 93 | @Inject(method = {"ret", "returnType"}, at = @At(value = "HEAD")) 94 | private void inject_ret(CallbackInfo ci) { 95 | 96 | if (!probeZS$enabled) { 97 | return; 98 | } 99 | 100 | if (probeZS$firstLabel == null) { 101 | probeZS$firstLabel = new Label(); 102 | visitor.visitLabel(probeZS$firstLabel); 103 | 104 | if (ParsedFunctionHooks.currentFunction.get() != null) { 105 | ZenPosition position = ParsedFunctionHooks.currentFunction.get().getPosition(); 106 | visitor.visitLineNumber(position.getLine(), probeZS$firstLabel); 107 | } 108 | } 109 | } 110 | 111 | @Inject(method = "start", at = @At(value = "HEAD")) 112 | private void inject_start(CallbackInfo ci) { 113 | ParsedFunctionHooks.isGeneratingStatement.set(true); 114 | } 115 | 116 | @Inject(method = "end", at = @At(value = "HEAD")) 117 | private void inject_end(CallbackInfo ci) { 118 | ParsedFunctionHooks.currentFunction.remove(); 119 | ParsedFunctionHooks.isGeneratingStatement.set(false); 120 | if (!probeZS$enabled) { 121 | return; 122 | } 123 | 124 | probeZS$endScope(); 125 | 126 | probeZS$lastLabel = new Label(); 127 | 128 | label(probeZS$lastLabel); 129 | 130 | 131 | probeZS$finishVariables(); 132 | 133 | for (LocalVariable variable : probeZS$variables) { 134 | String name = variable.getName(); 135 | String desc = variable.getType().getDescriptor(); 136 | int local = variable.getIdxSupplier().getAsInt(); 137 | Label begin = variable.getScopeBegin(); 138 | if (begin == null) { 139 | begin = this.probeZS$firstLabel; 140 | } 141 | Label end = variable.getScopeEnd(); 142 | if (begin != null && end != null && begin != end) { 143 | visitor.visitLocalVariable(name, desc, null, begin, end, local); 144 | } 145 | } 146 | } 147 | 148 | @Inject(method = "position", 149 | at = @At(value = "INVOKE", target = "Lorg/objectweb/asm/commons/LocalVariablesSorter;visitLabel(Lorg/objectweb/asm/Label;)V"), 150 | locals = LocalCapture.CAPTURE_FAILEXCEPTION 151 | ) 152 | private void inject_position(ZenPosition position, CallbackInfo ci, Label label) { 153 | if (probeZS$firstLabel == null) { 154 | probeZS$firstLabel = label; 155 | } 156 | 157 | probeZS$lastLabel = label; 158 | 159 | probeZS$finishVariables(); 160 | } 161 | 162 | @Override 163 | public void probeZS$addVariable(LocalVariable variable) { 164 | if (!probeZS$enabled) { 165 | return; 166 | } 167 | 168 | variable.setScopeBegin(probeZS$lastLabel); 169 | 170 | Objects.requireNonNull(probeZS$scopes.peek()).getVariables().add(variable); 171 | } 172 | 173 | @Override 174 | public void probeZS$injectThis(ParsedZenClass zenClass) { 175 | if (!probeZS$enabled) { 176 | return; 177 | } 178 | Scope rootScope = probeZS$scopes.peekLast(); 179 | if (zenClass != null && rootScope != null) { 180 | // add 'this' 181 | Type asmType = zenClass.type.toASMType(); 182 | LocalVariable thisVar = new LocalVariable("this", asmType, () -> 0); 183 | rootScope.getVariables().add(thisVar); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/mixin/MixinParsedFunction.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | import stanhebben.zenscript.definitions.ParsedFunction; 8 | import stanhebben.zenscript.statements.Statement; 9 | import youyihj.probezs.core.hook.ParsedFunctionHooks; 10 | 11 | @Mixin(value = ParsedFunction.class, remap = false) 12 | public abstract class MixinParsedFunction { 13 | 14 | @Inject(method = "getStatements", at = @At(value = "HEAD")) 15 | private void inject_getStatements(CallbackInfoReturnable cir) { 16 | if (ParsedFunctionHooks.isGeneratingStatement.get()) { 17 | ParsedFunctionHooks.currentFunction.remove(); 18 | ParsedFunctionHooks.currentFunction.set((ParsedFunction) (Object) this); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/mixin/MixinParsedZenClass.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.mixin; 2 | 3 | 4 | import org.objectweb.asm.ClassWriter; 5 | import org.spongepowered.asm.mixin.Final; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | import stanhebben.zenscript.compiler.EnvironmentClass; 12 | import stanhebben.zenscript.definitions.zenclasses.ParsedZenClass; 13 | import stanhebben.zenscript.type.ZenTypeZenClass; 14 | import youyihj.probezs.core.hook.ParsedZenClassHooks; 15 | 16 | @Mixin(value = ParsedZenClass.class, remap = false) 17 | public abstract class MixinParsedZenClass { 18 | 19 | @Shadow 20 | @Final 21 | public ZenTypeZenClass type; 22 | 23 | @Inject(method = "writeMethods", at = @At("HEAD")) 24 | private void inject_writeMethods_begin(ClassWriter newClass, EnvironmentClass environmentNewClass, CallbackInfo ci) { 25 | ParsedZenClassHooks.current.set(this.type); 26 | } 27 | 28 | @Inject(method = "writeConstructors", at = @At("HEAD")) 29 | private void inject_writeConstructors_begin(ClassWriter newClass, EnvironmentClass environmentNewClass, CallbackInfo ci) { 30 | ParsedZenClassHooks.current.set(this.type); 31 | } 32 | 33 | @Inject(method = "writeMethods", at = @At("RETURN")) 34 | private void inject_writeMethods_end(ClassWriter newClass, EnvironmentClass environmentNewClass, CallbackInfo ci) { 35 | ParsedZenClassHooks.current.remove(); 36 | } 37 | 38 | @Inject(method = "writeConstructors", at = @At("RETURN")) 39 | private void inject_writeConstructors_end(ClassWriter newClass, EnvironmentClass environmentNewClass, CallbackInfo ci) { 40 | ParsedZenClassHooks.current.remove(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/mixin/MixinStatementBlockOrForeach.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.mixin; 2 | 3 | 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | import stanhebben.zenscript.compiler.IEnvironmentMethod; 9 | import stanhebben.zenscript.statements.StatementBlock; 10 | import stanhebben.zenscript.statements.StatementForeach; 11 | import youyihj.probezs.core.hook.ILocalVariableMethodOutput; 12 | 13 | @Mixin(value = { 14 | StatementBlock.class, 15 | StatementForeach.class 16 | }, remap = false) 17 | public abstract class MixinStatementBlockOrForeach { 18 | 19 | @Inject(method = "compile(Lstanhebben/zenscript/compiler/IEnvironmentMethod;)V", at = @At("HEAD")) 20 | private void inject_compile_start(IEnvironmentMethod environment, CallbackInfo ci) { 21 | ILocalVariableMethodOutput output = (ILocalVariableMethodOutput) environment.getOutput(); 22 | output.probeZS$beginScope(); 23 | } 24 | 25 | @Inject(method = "compile(Lstanhebben/zenscript/compiler/IEnvironmentMethod;)V", at = @At("RETURN")) 26 | private void inject_compile_return(IEnvironmentMethod environment, CallbackInfo ci) { 27 | ILocalVariableMethodOutput output = (ILocalVariableMethodOutput) environment.getOutput(); 28 | output.probeZS$endScope(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/core/mixin/SymbolArgumentAccessor.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.core.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Accessor; 5 | import stanhebben.zenscript.symbols.SymbolArgument; 6 | import stanhebben.zenscript.type.ZenType; 7 | 8 | @Mixin(value = SymbolArgument.class, remap = false) 9 | public interface SymbolArgumentAccessor { 10 | 11 | 12 | @Accessor 13 | int getId(); 14 | 15 | @Accessor 16 | ZenType getType(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/docs/BracketReturnTypes.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.docs; 2 | 3 | import crafttweaker.zenscript.IBracketHandler; 4 | import org.yaml.snakeyaml.Yaml; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.Map; 9 | 10 | /** 11 | * @author youyihj 12 | */ 13 | public class BracketReturnTypes { 14 | private static Map values; 15 | 16 | public static void load(String path) { 17 | Yaml yaml = new Yaml(); 18 | try (InputStream inputStream = BracketReturnTypes.class.getClassLoader().getResourceAsStream(path)) { 19 | values = yaml.loadAs(inputStream, Map.class); 20 | } catch (IOException e) { 21 | throw new RuntimeException(e); 22 | } 23 | } 24 | 25 | public static Class find(IBracketHandler bracketHandler) { 26 | if (values == null) { 27 | load("mappings/bracket-return-types.yaml"); 28 | } 29 | String type = values.get(bracketHandler.getClass().getCanonicalName()); 30 | if (type == null) { 31 | return bracketHandler.getReturnedClass(); 32 | } else { 33 | try { 34 | return Class.forName(type); 35 | } catch (ClassNotFoundException e) { 36 | return null; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/docs/ParameterNameMappings.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.docs; 2 | 3 | import org.yaml.snakeyaml.Yaml; 4 | import youyihj.probezs.ProbeZS; 5 | import youyihj.probezs.member.ExecutableData; 6 | import youyihj.probezs.util.LoadingObject; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Objects; 13 | import java.util.StringJoiner; 14 | 15 | public class ParameterNameMappings { 16 | private static LoadingObject>>> nameMappings; 17 | 18 | public static void load(String path) { 19 | Yaml yaml = new Yaml(); 20 | if (ProbeZS.instance.mappings.get().isEmpty()) { 21 | try (InputStream inputStream = ParameterNameMappings.class.getClassLoader().getResourceAsStream(path)) { 22 | nameMappings = LoadingObject.of(yaml.loadAs(inputStream, Map.class)); 23 | } catch (IOException e) { 24 | throw new RuntimeException(e); 25 | } 26 | } else { 27 | nameMappings = LoadingObject.of(yaml.loadAs(ProbeZS.instance.mappings.get(), Map.class)); 28 | } 29 | } 30 | 31 | public static List find(ExecutableData method) { 32 | if (nameMappings == null) { 33 | load("mappings/method-parameter-names.yaml"); 34 | } 35 | String clazzName = method.getDecalredClass().getCanonicalName(); 36 | List> datas = nameMappings.get().get(clazzName); 37 | 38 | if (method.getParameterTypes().length == 0) { 39 | return null; 40 | } 41 | if (datas == null) { 42 | return null; 43 | } 44 | String name = method.getName(); 45 | StringJoiner joiner = new StringJoiner(","); 46 | for (Class parameterType : method.getParameterTypes()) { 47 | joiner.add(parameterType.getTypeName()); 48 | } 49 | for (Map data : datas) { 50 | if (Objects.equals(name, data.get("name")) && Objects.equals(joiner.toString(), data.get("paramsSignature"))) { 51 | List result = (List) data.get("paramNames"); 52 | if (result.size() == method.getParameterCount()) { 53 | return result; 54 | } 55 | } 56 | } 57 | return null; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/AnnotatedMember.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member; 2 | 3 | import java.lang.annotation.Annotation; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | public interface AnnotatedMember { 9 | boolean isAnnotationPresent(Class annotationClass); 10 | 11 | A getAnnotation(Class annotationClass); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/ExecutableData.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | public interface ExecutableData extends AnnotatedMember { 9 | String getName(); 10 | 11 | Class getDecalredClass(); 12 | 13 | Class[] getParameterTypes(); 14 | 15 | int getParameterCount(); 16 | 17 | int getModifiers(); 18 | 19 | Type getReturnType(); 20 | 21 | ParameterData[] getParameters(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/FieldData.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | public interface FieldData extends AnnotatedMember { 9 | Class getDecalredClass(); 10 | 11 | String getName(); 12 | 13 | int getModifiers(); 14 | 15 | Type getType(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/MemberFactory.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member; 2 | 3 | import java.lang.reflect.Executable; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Parameter; 6 | 7 | /** 8 | * @author youyihj 9 | */ 10 | public interface MemberFactory { 11 | FieldData[] getFields(Class clazz); 12 | 13 | ExecutableData[] getMethods(Class clazz); 14 | 15 | ExecutableData[] getConstructors(Class clazz); 16 | 17 | ExecutableData reflect(Executable executable); 18 | 19 | FieldData reflect(Field field); 20 | 21 | ParameterData reflect(Parameter parameter); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/ParameterData.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | public interface ParameterData extends AnnotatedMember { 9 | String getName(); 10 | 11 | Class getType(); 12 | 13 | Type getGenericType(); 14 | 15 | boolean isVarargs(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/asm/ASMAnnotatedMember.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.asm; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.AnnotationNode; 5 | import youyihj.probezs.member.AnnotatedMember; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.lang.reflect.Proxy; 9 | import java.util.List; 10 | import java.util.Objects; 11 | 12 | /** 13 | * @author youyihj 14 | */ 15 | public class ASMAnnotatedMember implements AnnotatedMember { 16 | private final List annotationNodes; 17 | protected final ASMMemberFactory memberFactory; 18 | 19 | public ASMAnnotatedMember(List annotationNodes, ASMMemberFactory memberFactory) { 20 | this.annotationNodes = annotationNodes; 21 | this.memberFactory = memberFactory; 22 | } 23 | 24 | @Override 25 | public boolean isAnnotationPresent(Class annotationClass) { 26 | if (annotationNodes != null) { 27 | for (AnnotationNode annotation : annotationNodes) { 28 | if (Objects.equals(annotation.desc, org.objectweb.asm.Type.getDescriptor(annotationClass))) { 29 | return true; 30 | } 31 | } 32 | } 33 | return false; 34 | } 35 | 36 | @Override 37 | @SuppressWarnings("unchecked") 38 | public A getAnnotation(Class annotationClass) { 39 | if (annotationNodes != null) { 40 | for (AnnotationNode annotation : annotationNodes) { 41 | if (Objects.equals(annotation.desc, org.objectweb.asm.Type.getDescriptor(annotationClass))) { 42 | return (A) Proxy.newProxyInstance(memberFactory.getClassLoader(), new Class[]{annotationClass}, ((proxy, method, args) -> { 43 | List values = annotation.values; 44 | if (values != null) { 45 | for (int i = 0; i < values.size(); i += 2) { 46 | if (Objects.equals(values.get(i), method.getName())) { 47 | return parseAnnotationValue(values.get(i + 1)); 48 | } 49 | } 50 | } 51 | if (method.getName().equals("annotationType")) { 52 | return annotationClass; 53 | } 54 | return method.getDefaultValue(); 55 | })); 56 | } 57 | } 58 | } 59 | return null; 60 | } 61 | 62 | @SuppressWarnings({"unchecked", "rawtypes"}) 63 | private Object parseAnnotationValue(Object value) throws Exception { 64 | if (value.getClass() == Type.class) { 65 | return memberFactory.getTypeDescResolver().convertASMType(((Type) value)); 66 | } 67 | if (value instanceof List) { 68 | List list = (List) value; 69 | Object[] values = new Object[list.size()]; 70 | for (int i = 0; i < list.size(); i++) { 71 | values[i] = parseAnnotationValue(list.get(i)); 72 | } 73 | return values; 74 | } 75 | if (value instanceof String[]) { 76 | String[] strings = (String[]) value; 77 | return Enum.valueOf(((Class) Class.forName(Type.getType(strings[0]).getClassName(), true, memberFactory.getClassLoader())), strings[1]); 78 | } 79 | return value; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/asm/ASMField.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.asm; 2 | 3 | import org.objectweb.asm.tree.FieldNode; 4 | import youyihj.probezs.member.FieldData; 5 | 6 | import java.lang.reflect.Type; 7 | 8 | /** 9 | * @author youyihj 10 | */ 11 | public class ASMField extends ASMAnnotatedMember implements FieldData { 12 | private final FieldNode fieldNode; 13 | 14 | private final Class decalredClass; 15 | 16 | public ASMField(FieldNode fieldNode, ASMMemberFactory memberFactory, Class decalredClass) { 17 | super(fieldNode.visibleAnnotations, memberFactory); 18 | this.fieldNode = fieldNode; 19 | this.decalredClass = decalredClass; 20 | } 21 | 22 | @Override 23 | public Class getDecalredClass() { 24 | return decalredClass; 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return fieldNode.name; 30 | } 31 | 32 | @Override 33 | public int getModifiers() { 34 | return fieldNode.access; 35 | } 36 | 37 | @Override 38 | public Type getType() { 39 | return memberFactory.getTypeDescResolver().resolveTypeDesc(fieldNode.desc); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/asm/ASMMemberFactory.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.asm; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.AnnotationNode; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.objectweb.asm.tree.FieldNode; 7 | import org.objectweb.asm.tree.MethodNode; 8 | import youyihj.probezs.member.ExecutableData; 9 | import youyihj.probezs.member.FieldData; 10 | import youyihj.probezs.member.MemberFactory; 11 | import youyihj.probezs.member.ParameterData; 12 | import youyihj.probezs.member.reflection.JavaExecutable; 13 | import youyihj.probezs.member.reflection.JavaField; 14 | import youyihj.probezs.member.reflection.JavaParameter; 15 | import youyihj.probezs.util.Arrays; 16 | 17 | import java.lang.reflect.Executable; 18 | import java.lang.reflect.Field; 19 | import java.lang.reflect.Parameter; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.Set; 23 | import java.util.function.Predicate; 24 | import java.util.function.Supplier; 25 | 26 | /** 27 | * @author youyihj 28 | */ 29 | public class ASMMemberFactory implements MemberFactory { 30 | private final Set classAnnotationFilter; 31 | private final Map classes = new HashMap<>(); 32 | 33 | // double supplier to avoid early class loading 34 | private final Supplier> classLoader; 35 | private final TypeResolver typeResolver; 36 | 37 | public ASMMemberFactory(Set classAnnotationFilter, Supplier> classLoader) { 38 | this.classAnnotationFilter = classAnnotationFilter; 39 | this.classLoader = classLoader; 40 | this.typeResolver = new TypeResolver(this); 41 | } 42 | 43 | public void putClassNode(ClassNode classNode) { 44 | if (classNode.visibleAnnotations != null) { 45 | for (AnnotationNode visibleAnnotation : classNode.visibleAnnotations) { 46 | for (String s : classAnnotationFilter) { 47 | if (visibleAnnotation.desc.contains(s)) { 48 | classes.put(classNode.name, classNode); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | @Override 56 | public FieldData[] getFields(Class clazz) { 57 | ClassNode classNode = classes.get(Type.getInternalName(clazz)); 58 | if (classNode != null) { 59 | return Arrays.map(classNode.fields.toArray(new FieldNode[0]), FieldData.class, it -> new ASMField(it, this, clazz)); 60 | } 61 | return new FieldData[0]; 62 | } 63 | 64 | @Override 65 | public ExecutableData[] getMethods(Class clazz) { 66 | return getExecutables(clazz, false); 67 | } 68 | 69 | @Override 70 | public ExecutableData[] getConstructors(Class clazz) { 71 | return getExecutables(clazz, true); 72 | } 73 | 74 | @Override 75 | public ExecutableData reflect(Executable executable) { 76 | return new JavaExecutable(executable); 77 | } 78 | 79 | @Override 80 | public FieldData reflect(Field field) { 81 | return new JavaField(field); 82 | } 83 | 84 | @Override 85 | public ParameterData reflect(Parameter parameter) { 86 | return new JavaParameter(parameter); 87 | } 88 | 89 | public ClassLoader getClassLoader() { 90 | return classLoader.get().get(); 91 | } 92 | 93 | TypeResolver getTypeDescResolver() { 94 | return typeResolver; 95 | } 96 | 97 | private ExecutableData[] getExecutables(Class clazz, boolean constructor) { 98 | ClassNode classNode = classes.get(Type.getInternalName(clazz)); 99 | Predicate isConstructor = it -> "".equals(it.name); 100 | if (classNode != null) { 101 | return classNode.methods.stream() 102 | .filter(constructor ? isConstructor : isConstructor.negate()) 103 | .map(it -> new ASMMethod(it, this, clazz)) 104 | .toArray(ExecutableData[]::new); 105 | } 106 | return new ExecutableData[0]; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/asm/ASMMethod.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.asm; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.AnnotationNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import youyihj.probezs.member.ExecutableData; 7 | import youyihj.probezs.member.ParameterData; 8 | import youyihj.probezs.util.Arrays; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author youyihj 14 | */ 15 | public class ASMMethod extends ASMAnnotatedMember implements ExecutableData { 16 | private final MethodNode methodNode; 17 | private final Class decalredClass; 18 | 19 | public ASMMethod(MethodNode methodNode, ASMMemberFactory memberFactory, Class decalredClass) { 20 | super(methodNode.visibleAnnotations, memberFactory); 21 | this.methodNode = methodNode; 22 | this.decalredClass = decalredClass; 23 | } 24 | 25 | @Override 26 | public String getName() { 27 | return methodNode.name; 28 | } 29 | 30 | @Override 31 | public Class getDecalredClass() { 32 | return decalredClass; 33 | } 34 | 35 | @Override 36 | public Class[] getParameterTypes() { 37 | Type[] types = Type.getType(methodNode.desc).getArgumentTypes(); 38 | return Arrays.map(types, Class.class, memberFactory.getTypeDescResolver()::convertASMType); 39 | } 40 | 41 | @Override 42 | public int getParameterCount() { 43 | TypeResolver typeResolver = memberFactory.getTypeDescResolver(); 44 | if (methodNode.signature != null) { 45 | return typeResolver.resolveMethodArguments(methodNode.signature).size(); 46 | } else { 47 | return org.objectweb.asm.Type.getType(methodNode.desc).getArgumentTypes().length; 48 | } 49 | } 50 | 51 | @Override 52 | public int getModifiers() { 53 | return methodNode.access; 54 | } 55 | 56 | @Override 57 | public java.lang.reflect.Type getReturnType() { 58 | TypeResolver typeResolver = memberFactory.getTypeDescResolver(); 59 | if (methodNode.signature != null) { 60 | return typeResolver.resolveTypeDesc(typeResolver.resolveMethodReturnType(methodNode.signature)); 61 | } else { 62 | return typeResolver.resolveTypeDesc(methodNode.desc.substring(methodNode.desc.indexOf(')') + 1)); 63 | } 64 | } 65 | 66 | @Override 67 | public ParameterData[] getParameters() { 68 | ParameterData[] parameterData = new ParameterData[getParameterCount()]; 69 | for (int i = 0; i < parameterData.length; i++) { 70 | List[] parameterAnnotations = methodNode.visibleParameterAnnotations; 71 | List annotationNodes = parameterAnnotations != null ? parameterAnnotations[i] : null; 72 | parameterData[i] = new ASMParameter(this, methodNode, i, memberFactory, annotationNodes); 73 | } 74 | return parameterData; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/asm/ASMParameter.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.asm; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.tree.AnnotationNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import youyihj.probezs.member.ParameterData; 7 | 8 | import java.lang.reflect.Type; 9 | import java.util.List; 10 | 11 | /** 12 | * @author youyihj 13 | */ 14 | public class ASMParameter extends ASMAnnotatedMember implements ParameterData { 15 | private final MethodNode methodNode; 16 | private final int index; 17 | private final ASMMethod method; 18 | 19 | public ASMParameter(ASMMethod method, MethodNode methodNode, int index, ASMMemberFactory memberFactory, List annotationNode) { 20 | super(annotationNode, memberFactory); 21 | this.method = method; 22 | this.methodNode = methodNode; 23 | this.index = index; 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return "arg" + index; 29 | } 30 | 31 | @Override 32 | public Class getType() { 33 | return method.getParameterTypes()[index]; 34 | } 35 | 36 | @Override 37 | public Type getGenericType() { 38 | TypeResolver typeResolver = memberFactory.getTypeDescResolver(); 39 | if (methodNode.signature != null) { 40 | return typeResolver.resolveTypeDesc(typeResolver.resolveMethodArguments(methodNode.signature).get(index)); 41 | } else { 42 | return getType(); 43 | } 44 | } 45 | 46 | @Override 47 | public boolean isVarargs() { 48 | return (methodNode.access & Opcodes.ACC_VARARGS) != 0 && index == method.getParameterCount() - 1; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/asm/TypeResolver.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.asm; 2 | 3 | import com.google.common.base.Suppliers; 4 | import com.google.common.reflect.TypeToken; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | import org.objectweb.asm.ClassWriter; 8 | import org.objectweb.asm.MethodVisitor; 9 | import org.objectweb.asm.Opcodes; 10 | import youyihj.probezs.core.BytecodeClassLoader; 11 | 12 | import java.lang.reflect.Array; 13 | import java.lang.reflect.Type; 14 | import java.util.*; 15 | import java.util.function.Supplier; 16 | 17 | import static org.objectweb.asm.Opcodes.*; 18 | import static org.objectweb.asm.Type.DOUBLE; 19 | import static org.objectweb.asm.Type.FLOAT; 20 | import static org.objectweb.asm.Type.LONG; 21 | import static org.objectweb.asm.Type.*; 22 | 23 | /** 24 | * Simple type reader from generic signature, it can't handle type variable. 25 | * @author youyihj 26 | */ 27 | public class TypeResolver { 28 | 29 | private static final Logger LOGGER = LogManager.getLogger(); 30 | 31 | private final Supplier classLoader; 32 | private final Map results = new HashMap<>(); 33 | 34 | private int tokenIndex; 35 | 36 | public TypeResolver(ASMMemberFactory memberFactory) { 37 | this.classLoader = Suppliers.memoize(() -> new BytecodeClassLoader(memberFactory.getClassLoader())); 38 | results.put("V", void.class); 39 | results.put("Z", boolean.class); 40 | results.put("C", char.class); 41 | results.put("B", byte.class); 42 | results.put("I", int.class); 43 | results.put("F", float.class); 44 | results.put("J", long.class); 45 | results.put("D", double.class); 46 | results.put("S", short.class); 47 | } 48 | 49 | public Type resolveTypeDesc(String desc) { 50 | return results.computeIfAbsent(desc, this::buildType); 51 | } 52 | 53 | public List resolveMethodArguments(String methodSignature) { 54 | return new MethodParameterParser(methodSignature).parse(); 55 | } 56 | 57 | public String resolveMethodReturnType(String methodSignature) { 58 | String returnAndException = methodSignature.substring(methodSignature.indexOf(')') + 1); 59 | if (returnAndException.contains("^")) { 60 | return methodSignature.substring(0, methodSignature.indexOf('^')); 61 | } else { 62 | return returnAndException; 63 | } 64 | } 65 | 66 | Class convertASMType(org.objectweb.asm.Type type) { 67 | try { 68 | switch (type.getSort()) { 69 | case VOID: 70 | return void.class; 71 | case BOOLEAN: 72 | return boolean.class; 73 | case BYTE: 74 | return byte.class; 75 | case SHORT: 76 | return short.class; 77 | case INT: 78 | return int.class; 79 | case FLOAT: 80 | return float.class; 81 | case LONG: 82 | return long.class; 83 | case DOUBLE: 84 | return double.class; 85 | case ARRAY: 86 | org.objectweb.asm.Type elementType = type.getElementType(); 87 | int dimensions = type.getDimensions(); 88 | return Array.newInstance(convertASMType(elementType), new int[dimensions]).getClass(); 89 | case OBJECT: 90 | return Class.forName(type.getClassName(), false, classLoader.get()); 91 | default: 92 | return Object.class; 93 | } 94 | } catch (ClassNotFoundException e) { 95 | LOGGER.throwing(e); 96 | return Object.class; 97 | } 98 | } 99 | 100 | @SuppressWarnings("all") 101 | private Type buildType(String desc) { 102 | String className = "TypeToken" + tokenIndex++; 103 | ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 104 | classWriter.visit(V1_8, ACC_PUBLIC, className, 105 | "Lcom/google/common/reflect/TypeToken<" + desc + ">;", 106 | "com/google/common/reflect/TypeToken", null); 107 | MethodVisitor constructor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); 108 | constructor.visitCode(); 109 | constructor.visitVarInsn(ALOAD, 0); 110 | constructor.visitMethodInsn(INVOKESPECIAL, "com/google/common/reflect/TypeToken", "", "()V", false); 111 | constructor.visitInsn(RETURN); 112 | constructor.visitMaxs(1, 1); 113 | constructor.visitEnd(); 114 | classWriter.visitEnd(); 115 | classLoader.get().putBytecode(className, classWriter.toByteArray()); 116 | try { 117 | return Objects.requireNonNull(((TypeToken) Class.forName(className, true, classLoader.get()).newInstance()).getType()); 118 | } catch (Throwable e) { 119 | LOGGER.error("Failed get type from desc: " + desc); 120 | return Object.class; 121 | } 122 | } 123 | 124 | static final class MethodParameterParser { 125 | private int layer = 0; 126 | private final List paramTypes = new ArrayList<>(); 127 | private StringBuilder sb = new StringBuilder(); 128 | 129 | private final String signature; 130 | 131 | public MethodParameterParser(String signature) { 132 | this.signature = signature; 133 | } 134 | 135 | List parse() { 136 | String params = signature.substring(signature.indexOf('(') + 1, signature.indexOf(')')); 137 | boolean readingLongType = false; 138 | 139 | for (char c : params.toCharArray()) { 140 | sb.append(c); 141 | switch (c) { 142 | case 'V': 143 | case 'Z': 144 | case 'C': 145 | case 'B': 146 | case 'I': 147 | case 'F': 148 | case 'J': 149 | case 'D': 150 | if (!readingLongType && layer == 0) { 151 | endType(); 152 | } 153 | break; 154 | case 'T': 155 | case 'L': 156 | if (!readingLongType) { 157 | readingLongType = true; 158 | layer++; 159 | } 160 | break; 161 | case '<': 162 | case '>': 163 | readingLongType = false; 164 | break; 165 | case ';': 166 | layer--; 167 | readingLongType = false; 168 | if (layer == 0) { 169 | endType(); 170 | } 171 | break; 172 | } 173 | } 174 | return paramTypes; 175 | } 176 | 177 | private void endType() { 178 | paramTypes.add(sb.toString()); 179 | sb = new StringBuilder(); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/reflection/JavaExecutable.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.reflection; 2 | 3 | import youyihj.probezs.member.ExecutableData; 4 | import youyihj.probezs.member.ParameterData; 5 | import youyihj.probezs.util.Arrays; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.lang.reflect.*; 9 | 10 | /** 11 | * @author youyihj 12 | */ 13 | public class JavaExecutable implements ExecutableData { 14 | private final Executable executable; 15 | 16 | public JavaExecutable(Executable executable) { 17 | this.executable = executable; 18 | } 19 | 20 | @Override 21 | public boolean isAnnotationPresent(Class annotationClass) { 22 | return executable.isAnnotationPresent(annotationClass); 23 | } 24 | 25 | @Override 26 | public A getAnnotation(Class annotationClass) { 27 | return executable.getAnnotation(annotationClass); 28 | } 29 | 30 | @Override 31 | public String getName() { 32 | return executable instanceof Constructor ? "" : executable.getName(); 33 | } 34 | 35 | @Override 36 | public Class getDecalredClass() { 37 | return executable.getDeclaringClass(); 38 | } 39 | 40 | @Override 41 | public Class[] getParameterTypes() { 42 | return executable.getParameterTypes(); 43 | } 44 | 45 | @Override 46 | public int getParameterCount() { 47 | return executable.getParameterCount(); 48 | } 49 | 50 | @Override 51 | public int getModifiers() { 52 | return executable.getModifiers(); 53 | } 54 | 55 | @Override 56 | public Type getReturnType() { 57 | return executable instanceof Method ? ((Method) executable).getGenericReturnType() : getDecalredClass(); 58 | } 59 | 60 | @Override 61 | public ParameterData[] getParameters() { 62 | return Arrays.map(executable.getParameters(), ParameterData.class, JavaParameter::new); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/reflection/JavaField.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.reflection; 2 | 3 | import youyihj.probezs.member.FieldData; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Type; 8 | 9 | /** 10 | * @author youyihj 11 | */ 12 | public class JavaField implements FieldData { 13 | private final Field field; 14 | 15 | public JavaField(Field field) { 16 | this.field = field; 17 | } 18 | 19 | @Override 20 | public boolean isAnnotationPresent(Class annotationClass) { 21 | return field.isAnnotationPresent(annotationClass); 22 | } 23 | 24 | @Override 25 | public A getAnnotation(Class annotationClass) { 26 | return field.getAnnotation(annotationClass); 27 | } 28 | 29 | @Override 30 | public Class getDecalredClass() { 31 | return field.getDeclaringClass(); 32 | } 33 | 34 | @Override 35 | public String getName() { 36 | return field.getName(); 37 | } 38 | 39 | @Override 40 | public int getModifiers() { 41 | return field.getModifiers(); 42 | } 43 | 44 | @Override 45 | public Type getType() { 46 | return field.getGenericType(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/reflection/JavaParameter.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.reflection; 2 | 3 | import youyihj.probezs.member.ParameterData; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Parameter; 7 | import java.lang.reflect.Type; 8 | 9 | /** 10 | * @author youyihj 11 | */ 12 | public class JavaParameter implements ParameterData { 13 | private final Parameter parameter; 14 | 15 | public JavaParameter(Parameter parameter) { 16 | this.parameter = parameter; 17 | } 18 | 19 | @Override 20 | public boolean isAnnotationPresent(Class annotationClass) { 21 | return parameter.isAnnotationPresent(annotationClass); 22 | } 23 | 24 | @Override 25 | public A getAnnotation(Class annotationClass) { 26 | return parameter.getAnnotation(annotationClass); 27 | } 28 | 29 | @Override 30 | public String getName() { 31 | return parameter.getName(); 32 | } 33 | 34 | @Override 35 | public Class getType() { 36 | return parameter.getType(); 37 | } 38 | 39 | @Override 40 | public Type getGenericType() { 41 | return parameter.getParameterizedType(); 42 | } 43 | 44 | @Override 45 | public boolean isVarargs() { 46 | return parameter.isVarArgs(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/member/reflection/ReflectionMemberFactory.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.member.reflection; 2 | 3 | import youyihj.probezs.member.ExecutableData; 4 | import youyihj.probezs.member.FieldData; 5 | import youyihj.probezs.member.MemberFactory; 6 | import youyihj.probezs.member.ParameterData; 7 | import youyihj.probezs.util.Arrays; 8 | 9 | import java.lang.reflect.Executable; 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.Parameter; 12 | 13 | /** 14 | * @author youyihj 15 | */ 16 | public class ReflectionMemberFactory implements MemberFactory { 17 | @Override 18 | public FieldData[] getFields(Class clazz) { 19 | return Arrays.map(clazz.getDeclaredFields(), FieldData.class, JavaField::new); 20 | } 21 | 22 | @Override 23 | public ExecutableData[] getMethods(Class clazz) { 24 | return Arrays.map(clazz.getDeclaredMethods(), ExecutableData.class, JavaExecutable::new); 25 | } 26 | 27 | @Override 28 | public ExecutableData[] getConstructors(Class clazz) { 29 | return Arrays.map(clazz.getDeclaredConstructors(), ExecutableData.class, JavaExecutable::new); 30 | } 31 | 32 | @Override 33 | public ExecutableData reflect(Executable executable) { 34 | return new JavaExecutable(executable); 35 | } 36 | 37 | @Override 38 | public FieldData reflect(Field field) { 39 | return new JavaField(field); 40 | } 41 | 42 | @Override 43 | public ParameterData reflect(Parameter parameter) { 44 | return new JavaParameter(parameter); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/render/RenderHelper.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.render; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.gui.ScaledResolution; 5 | import net.minecraft.client.renderer.GlStateManager; 6 | import org.lwjgl.BufferUtils; 7 | import org.lwjgl.opengl.GL11; 8 | import org.lwjgl.opengl.GL12; 9 | 10 | import java.awt.*; 11 | import java.awt.geom.AffineTransform; 12 | import java.awt.image.BufferedImage; 13 | import java.nio.ByteBuffer; 14 | 15 | /** 16 | * This and other render code borrowed from @unascribed (licensed MIT) 17 | * https://github.com/unascribed/BlockRenderer 18 | */ 19 | public class RenderHelper { 20 | private static float oldZLevel; 21 | 22 | public static void setupRenderStateWithMul(float mul) { 23 | setupRenderState(16 * mul); 24 | } 25 | 26 | public static void setupRenderState(float desiredSize) { 27 | Minecraft mc = Minecraft.getMinecraft(); 28 | ScaledResolution res = new ScaledResolution(mc); 29 | 30 | // Switches from 3D to 2D 31 | mc.entityRenderer.setupOverlayRendering(); 32 | net.minecraft.client.renderer.RenderHelper.enableGUIStandardItemLighting(); 33 | /* 34 | * The GUI scale affects us due to the call to setupOverlayRendering 35 | * above. As such, we need to counteract this to always get a 512x512 36 | * render. We could manually switch to orthogonal mode, but it's just 37 | * more convenient to leverage setupOverlayRendering. 38 | */ 39 | float scale = desiredSize / (16f * res.getScaleFactor()); 40 | GlStateManager.translate(0, 0, -(scale * 100)); 41 | 42 | GlStateManager.scale(scale, scale, scale); 43 | 44 | oldZLevel = mc.getRenderItem().zLevel; 45 | mc.getRenderItem().zLevel = -50; 46 | 47 | GlStateManager.enableRescaleNormal(); 48 | GlStateManager.enableColorMaterial(); 49 | GlStateManager.enableDepth(); 50 | GlStateManager.enableBlend(); 51 | GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_SRC_ALPHA, GL11.GL_ONE); 52 | GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 53 | GlStateManager.disableAlpha(); 54 | } 55 | 56 | public static void tearDownRenderState() { 57 | GlStateManager.disableLighting(); 58 | GlStateManager.disableColorMaterial(); 59 | GlStateManager.disableDepth(); 60 | GlStateManager.disableBlend(); 61 | 62 | Minecraft.getMinecraft().getRenderItem().zLevel = oldZLevel; 63 | } 64 | 65 | public static BufferedImage readPixels(int width, int height) { 66 | /* 67 | * Make sure we're reading from the back buffer, not the front buffer. 68 | * The front buffer is what is currently on-screen, and is useful for 69 | * screenshots. 70 | */ 71 | GL11.glReadBuffer(GL11.GL_BACK); 72 | // Allocate a native data array to fit our pixels 73 | ByteBuffer buf = BufferUtils.createByteBuffer(width * height * 4); 74 | // And finally read the pixel data from the GPU... 75 | GL11.glReadPixels(0, Minecraft.getMinecraft().displayHeight - height, width, height, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, buf); 76 | // ...and turn it into a Java object we can do things to. 77 | BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 78 | int[] pixels = new int[width * height]; 79 | buf.asIntBuffer().get(pixels); 80 | img.setRGB(0, 0, width, height, pixels, 0, width); 81 | return img; 82 | } 83 | 84 | public static BufferedImage createFlipped(BufferedImage image) { 85 | AffineTransform at = new AffineTransform(); 86 | /* 87 | * Creates a compound affine transform, instead of just one, as we need 88 | * to perform two transformations. 89 | * 90 | * The first one is to scale the image to 100% width, and -100% height. 91 | * (That's *negative* 100%.) 92 | */ 93 | at.concatenate(AffineTransform.getScaleInstance(1, -1)); 94 | /* 95 | * We then need to translate the image back up by it's height, as flipping 96 | * it over moves it off the bottom of the canvas. 97 | */ 98 | at.concatenate(AffineTransform.getTranslateInstance(0, -image.getHeight())); 99 | return createTransformed(image, at); 100 | } 101 | 102 | public static BufferedImage createTransformed(BufferedImage image, AffineTransform at) { 103 | // Create a blank image with the same dimensions as the old one... 104 | BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); 105 | // ...get it's renderer... 106 | Graphics2D g = newImage.createGraphics(); 107 | /// ...and draw the old image on top of it with our transform. 108 | g.transform(at); 109 | g.drawImage(image, 0, 0, null); 110 | g.dispose(); 111 | return newImage; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/render/RenderTaskDispatcher.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.render; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 5 | import net.minecraftforge.fml.common.gameevent.TickEvent; 6 | 7 | import java.util.ArrayDeque; 8 | import java.util.Queue; 9 | import java.util.concurrent.Callable; 10 | import java.util.concurrent.Future; 11 | import java.util.concurrent.FutureTask; 12 | 13 | /** 14 | * @author youyihj 15 | */ 16 | @Mod.EventBusSubscriber 17 | public class RenderTaskDispatcher { 18 | private static final Queue> tasks = new ArrayDeque<>(); 19 | 20 | public static Future submit(Callable callable) { 21 | FutureTask task = new FutureTask<>(callable); 22 | synchronized (tasks) { 23 | tasks.add(task); 24 | return task; 25 | } 26 | } 27 | 28 | @SubscribeEvent 29 | public static void onRenderTickStart(TickEvent.RenderTickEvent event) { 30 | if (event.phase == TickEvent.Phase.START) { 31 | synchronized (tasks) { 32 | while (!tasks.isEmpty()) { 33 | tasks.poll().run(); 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/IHasImportMembers.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | public interface IHasImportMembers { 9 | void fillImportMembers(Set members); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/IMaybeExpansionMember.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | /** 4 | * @author youyihj 5 | */ 6 | public interface IMaybeExpansionMember { 7 | void setOwner(String owner); 8 | 9 | String getOwner(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/IZenDumpable.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import youyihj.probezs.util.IndentStringBuilder; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | public interface IZenDumpable { 9 | void toZenScript(IndentStringBuilder sb); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/JavaTypeMirror.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonPrimitive; 5 | import com.google.gson.JsonSerializationContext; 6 | import com.google.gson.JsonSerializer; 7 | import crafttweaker.util.IEventHandler; 8 | import youyihj.probezs.ProbeZS; 9 | import youyihj.probezs.util.IntersectionType; 10 | 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.*; 14 | import java.util.function.Supplier; 15 | import java.util.stream.Collectors; 16 | 17 | /** 18 | * @author youyihj 19 | */ 20 | public class JavaTypeMirror implements Supplier { 21 | private final Type type; 22 | private final ZenClassTree classTree; 23 | private Result result; 24 | 25 | private boolean init = false; 26 | private boolean existed = false; 27 | 28 | public JavaTypeMirror(Type type, ZenClassTree classTree) { 29 | this.type = type; 30 | this.classTree = classTree; 31 | } 32 | 33 | @Override 34 | public Result get() { 35 | if (init) { 36 | return result; 37 | } 38 | throw new IllegalStateException(); 39 | } 40 | 41 | public boolean isExisted() { 42 | if (init) { 43 | return existed; 44 | } 45 | throw new IllegalStateException(); 46 | } 47 | 48 | void fresh() { 49 | init = true; 50 | result = getResult(type); 51 | existed = !(result instanceof MissingResult); 52 | } 53 | 54 | private Result getResult(Type type) { 55 | Map, ZenClassNode> javaMap = classTree.getJavaMap(); 56 | try { 57 | if (type instanceof Class) { 58 | Class clazz = (Class) type; 59 | if (clazz.isArray()) { 60 | Result baseClass = getResult(clazz.getComponentType()); 61 | return Result.compound("%s[]", baseClass); 62 | } else { 63 | ZenClassNode zsClass = javaMap.get(type); 64 | // the class is exposed to zs 65 | if (zsClass != null) { 66 | return Result.single(zsClass); 67 | } 68 | // the class isn't exposed to zs, but its super class and implement interfaces may be exposed 69 | List> exposedParents = collectExposedParents(clazz, new ArrayList<>()); 70 | if (exposedParents.isEmpty()) { 71 | return Result.missing(classTree, clazz); 72 | } 73 | return Result.intersection( 74 | exposedParents.stream() 75 | .map(this::getResult) 76 | .collect(Collectors.toList()) 77 | ); 78 | } 79 | } else if (type instanceof ParameterizedType) { 80 | ParameterizedType parameterizedType = (ParameterizedType) type; 81 | Type[] arguments = parameterizedType.getActualTypeArguments(); 82 | if (parameterizedType.getRawType() == List.class) { 83 | Result baseClass = getResult(arguments[0]); 84 | return Result.compound("[%s]", baseClass); 85 | } 86 | if (parameterizedType.getRawType() == IEventHandler.class) { 87 | Result baseClass = getResult(arguments[0]); 88 | return Result.compound("function(%s)void", baseClass); 89 | } 90 | if (parameterizedType.getRawType() == Map.class) { 91 | Result keyClass = getResult(arguments[0]); 92 | Result valueClass = getResult(arguments[1]); 93 | return Result.compound("%s[%s]", valueClass, keyClass); 94 | } 95 | return getResult(parameterizedType.getRawType()); 96 | } else if (type instanceof IntersectionType) { 97 | Type[] compoundTypes = ((IntersectionType) type).getCompoundTypes(); 98 | return Result.intersection( 99 | Arrays.stream(compoundTypes) 100 | .map(this::getResult) 101 | .collect(Collectors.toList()) 102 | ); 103 | } 104 | } catch (Exception ignored) { 105 | } 106 | return Result.missing(classTree, type); 107 | } 108 | 109 | public List> collectExposedParents(Class clazz, List> list) { 110 | for (Class anInterface : clazz.getInterfaces()) { 111 | if (classTree.getJavaMap().containsKey(anInterface)) { 112 | addInteractionClasses(list, anInterface); 113 | } else { 114 | collectExposedParents(anInterface, list); 115 | } 116 | } 117 | if (!clazz.isInterface()) { 118 | Class superclass = clazz.getSuperclass(); 119 | if (superclass != null && superclass != Object.class) { 120 | if (classTree.getJavaMap().containsKey(superclass)) { 121 | addInteractionClasses(list, superclass); 122 | } else { 123 | collectExposedParents(superclass, list); 124 | } 125 | } 126 | } 127 | return list; 128 | } 129 | 130 | private void addInteractionClasses(List> classes, Class clazz) { 131 | boolean overlap = classes.stream().anyMatch(clazz::isAssignableFrom); 132 | if (!overlap) { 133 | classes.add(clazz); 134 | } 135 | } 136 | 137 | public interface Result { 138 | String getFullName(); 139 | 140 | String getQualifiedName(); 141 | 142 | List getTypeVariables(); 143 | 144 | static Result single(ZenClassNode classNode) { 145 | return new SingleResult(classNode); 146 | } 147 | 148 | static Result missing(ZenClassTree tree, Type originType) { 149 | ProbeZS.logger.warn("Do not know zenscript type for {}", originType.getTypeName()); 150 | return new MissingResult(tree); 151 | } 152 | 153 | static Result compound(String format, Result... results) { 154 | return new CompoundResult(format, results); 155 | } 156 | 157 | static Result intersection(List results) { 158 | return new IntersectionResult(results); 159 | } 160 | } 161 | 162 | private static class SingleResult implements Result { 163 | private final ZenClassNode classNode; 164 | 165 | public SingleResult(ZenClassNode classNode) { 166 | this.classNode = classNode; 167 | } 168 | 169 | @Override 170 | public String getFullName() { 171 | return classNode.getName(); 172 | } 173 | 174 | @Override 175 | public String getQualifiedName() { 176 | return classNode.getQualifiedName(); 177 | } 178 | 179 | @Override 180 | public List getTypeVariables() { 181 | return Collections.singletonList(classNode); 182 | } 183 | } 184 | 185 | 186 | private static class MissingResult implements Result { 187 | private final ZenClassTree tree; 188 | 189 | private MissingResult(ZenClassTree tree) { 190 | this.tree = tree; 191 | } 192 | 193 | @Override 194 | public String getFullName() { 195 | return "any"; 196 | } 197 | 198 | @Override 199 | public String getQualifiedName() { 200 | return "any"; 201 | } 202 | 203 | @Override 204 | public List getTypeVariables() { 205 | return Collections.singletonList(tree.getAnyClass()); 206 | } 207 | } 208 | 209 | private static class CompoundResult implements Result { 210 | private final String format; 211 | private final List results; 212 | 213 | public CompoundResult(String format, Result... results) { 214 | this.format = format; 215 | this.results = Arrays.asList(results); 216 | } 217 | 218 | @Override 219 | public String getFullName() { 220 | return String.format(format, results.stream().map(Result::getFullName).toArray()); 221 | } 222 | 223 | @Override 224 | public String getQualifiedName() { 225 | return String.format(format, results.stream().map(Result::getQualifiedName).toArray()); 226 | } 227 | 228 | @Override 229 | public List getTypeVariables() { 230 | return results.stream() 231 | .flatMap(it -> it.getTypeVariables().stream()) 232 | .distinct() 233 | .collect(Collectors.toList()); 234 | } 235 | } 236 | 237 | private static class IntersectionResult implements Result { 238 | private final List results; 239 | 240 | public IntersectionResult(List results) { 241 | this.results = results; 242 | } 243 | 244 | @Override 245 | public String getFullName() { 246 | return results.stream() 247 | .map(Result::getFullName) 248 | .collect(Collectors.joining(" & ")); 249 | } 250 | 251 | @Override 252 | public String getQualifiedName() { 253 | return results.stream() 254 | .map(Result::getQualifiedName) 255 | .collect(Collectors.joining(" & ")); 256 | } 257 | 258 | @Override 259 | public List getTypeVariables() { 260 | return results.stream() 261 | .flatMap(it -> it.getTypeVariables().stream()) 262 | .distinct() 263 | .collect(Collectors.toList()); 264 | } 265 | } 266 | 267 | public static class Serializer implements JsonSerializer> { 268 | 269 | @Override 270 | public JsonElement serialize(Supplier src, Type typeOfSrc, JsonSerializationContext context) { 271 | return new JsonPrimitive(src.get().getQualifiedName()); 272 | } 273 | } 274 | 275 | public static class FullNameSerializer implements JsonSerializer { 276 | 277 | @Override 278 | public JsonElement serialize(JavaTypeMirror src, Type typeOfSrc, JsonSerializationContext context) { 279 | return new JsonPrimitive(src.get().getFullName()); 280 | } 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/ZenClassTree.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.reflect.TypeToken; 6 | import crafttweaker.zenscript.expand.ExpandAnyArray; 7 | import crafttweaker.zenscript.expand.ExpandAnyDict; 8 | import crafttweaker.zenscript.expand.ExpandByteArray; 9 | import crafttweaker.zenscript.expand.ExpandIntArray; 10 | import stanhebben.zenscript.annotations.ZenClass; 11 | import stanhebben.zenscript.annotations.ZenExpansion; 12 | import stanhebben.zenscript.value.IntRange; 13 | import youyihj.probezs.ProbeZS; 14 | import youyihj.probezs.ProbeZSConfig; 15 | import youyihj.probezs.tree.primitive.*; 16 | import youyihj.probezs.util.FileUtils; 17 | import youyihj.probezs.util.IndentStringBuilder; 18 | import youyihj.probezs.util.LoadingObject; 19 | 20 | import java.io.IOException; 21 | import java.lang.reflect.Type; 22 | import java.nio.file.FileVisitResult; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.nio.file.SimpleFileVisitor; 26 | import java.nio.file.attribute.BasicFileAttributes; 27 | import java.util.*; 28 | import java.util.function.Supplier; 29 | 30 | /** 31 | * @author youyihj 32 | */ 33 | public class ZenClassTree { 34 | private static LoadingObject root; 35 | public static final Gson GSON = new GsonBuilder() 36 | .setPrettyPrinting() 37 | .registerTypeAdapter(new TypeToken>() { 38 | }.getType(), new JavaTypeMirror.Serializer()) 39 | .registerTypeAdapter(JavaTypeMirror.class, new JavaTypeMirror.Serializer()) 40 | .registerTypeAdapter(ZenClassNode.class, new ZenClassNode.Serializer()) 41 | .registerTypeAdapter(ZenParameterNode.class, new ZenParameterNode.Serializer()) 42 | .registerTypeAdapter(ZenOperatorNode.As.class, new ZenOperatorNode.AsSerializer()) 43 | .create(); 44 | 45 | private final Map classes = new LinkedHashMap<>(); 46 | private final Map, ZenClassNode> javaMap = new HashMap<>(); 47 | private final List javaTypeMirrors = new ArrayList<>(); 48 | 49 | private final Set> blackList = new HashSet<>(); 50 | private final ZenClassNode anyClass = new ZenAnyNode(this); 51 | 52 | public static ZenClassTree getRoot() { 53 | if (root == null) { 54 | root = LoadingObject.of(new ZenClassTree()); 55 | root.get().addBlackList( 56 | ExpandAnyDict.class, 57 | ExpandAnyArray.class, 58 | ExpandByteArray.class, 59 | ExpandIntArray.class 60 | ); 61 | } 62 | return root.get(); 63 | } 64 | 65 | public ZenClassTree() { 66 | registerPrimitiveClasses(); 67 | } 68 | 69 | public void putClass(Class clazz) { 70 | try { 71 | if (blackList.contains(clazz)) return; 72 | ZenClass zenClass = clazz.getAnnotation(ZenClass.class); 73 | ZenExpansion zenExpansion = clazz.getAnnotation(ZenExpansion.class); 74 | if (zenClass != null) { 75 | String name = zenClass.value(); 76 | if (name.isEmpty()) { 77 | name = clazz.getName(); 78 | } 79 | ZenClassNode classNode = classes.computeIfAbsent(name, it -> new ZenClassNode(it, this)); 80 | javaMap.put(clazz, classNode); 81 | classNode.readExtendClasses(clazz); 82 | classNode.readMembers(clazz, true); 83 | } 84 | if (zenExpansion != null) { 85 | String name = zenExpansion.value(); 86 | // don't export collection expansion yet, and avoid empty name 87 | if (name.contains("[") || name.isEmpty()) { 88 | return; 89 | } 90 | ZenClassNode classNode = classes.computeIfAbsent(name, it -> new ZenClassNode(it, this)); 91 | classNode.readMembers(clazz, false); 92 | } 93 | } catch (Throwable e) { 94 | throw new RuntimeException("Failed to get members of " + clazz.getName() + ", try setting MemberCollector to ASM in config?", e); 95 | } 96 | } 97 | 98 | public JavaTypeMirror createJavaTypeMirror(Type type) { 99 | JavaTypeMirror javaTypeMirror = new JavaTypeMirror(Objects.requireNonNull(type), this); 100 | javaTypeMirrors.add(javaTypeMirror); 101 | return javaTypeMirror; 102 | } 103 | 104 | public void fresh() { 105 | javaTypeMirrors.forEach(JavaTypeMirror::fresh); 106 | } 107 | 108 | public void addBlackList(Class... classes) { 109 | blackList.addAll(Arrays.asList(classes)); 110 | } 111 | 112 | public ZenClassNode getAnyClass() { 113 | return anyClass; 114 | } 115 | 116 | public Map, ZenClassNode> getJavaMap() { 117 | return javaMap; 118 | } 119 | 120 | public Map getClasses() { 121 | return classes; 122 | } 123 | 124 | public void output() { 125 | removeOldScripts(); 126 | for (ZenClassNode classNode : classes.values()) { 127 | String filePath = classNode.getName().replace('.', '/'); 128 | try { 129 | if (ProbeZSConfig.dumpDZS) { 130 | IndentStringBuilder builder = new IndentStringBuilder(); 131 | classNode.toZenScript(builder); 132 | FileUtils.createFile(ProbeZS.instance.generatedPath.resolve(filePath + ".dzs"), 133 | builder.toString() 134 | ); 135 | } 136 | if (ProbeZSConfig.dumpJson) { 137 | FileUtils.createFile( 138 | ProbeZS.instance.generatedPath.resolve(filePath + ".json"), 139 | GSON.toJson(classNode, ZenClassNode.class) 140 | ); 141 | } 142 | } catch (IOException e) { 143 | ProbeZS.logger.error("Failed to output: {} {}", classNode.getName(), e); 144 | } 145 | } 146 | } 147 | 148 | public void putGlobalInternalClass(Class clazz) { 149 | if (!javaMap.containsKey(clazz)) { 150 | ZenClassNode classNode = classes.computeIfAbsent(clazz.getName(), it -> new ZenClassNode(it, this)); 151 | javaMap.put(clazz, classNode); 152 | classNode.readMembers(clazz, true); 153 | } 154 | } 155 | 156 | private void registerPrimitiveClass(Class javaClass, ZenClassNode node) { 157 | classes.put(node.getName(), node); 158 | javaMap.put(javaClass, node); 159 | } 160 | 161 | private void registerPrimitiveClasses() { 162 | ZenClassNode intNode = new ZenIntNode(this); 163 | ZenClassNode longNode = new ZenLongNode(this); 164 | ZenClassNode byteNode = new ZenByteNode(this); 165 | ZenClassNode shortNode = new ZenShortNode(this); 166 | ZenClassNode booleanNode = new ZenBoolNode(this); 167 | ZenClassNode floatNode = new ZenFloatNode(this); 168 | ZenClassNode doubleNode = new ZenDoubleNode(this); 169 | ZenClassNode stringNode = new ZenStringNode(this); 170 | ZenClassNode voidNode = new ZenVoidNode(this); 171 | registerPrimitiveClass(int.class, intNode); 172 | registerPrimitiveClass(Integer.class, intNode); 173 | registerPrimitiveClass(long.class, longNode); 174 | registerPrimitiveClass(Long.class, longNode); 175 | registerPrimitiveClass(byte.class, byteNode); 176 | registerPrimitiveClass(Byte.class, byteNode); 177 | registerPrimitiveClass(short.class, shortNode); 178 | registerPrimitiveClass(Short.class, shortNode); 179 | registerPrimitiveClass(boolean.class, booleanNode); 180 | registerPrimitiveClass(Boolean.class, booleanNode); 181 | registerPrimitiveClass(float.class, floatNode); 182 | registerPrimitiveClass(Float.class, floatNode); 183 | registerPrimitiveClass(double.class, doubleNode); 184 | registerPrimitiveClass(Double.class, doubleNode); 185 | javaMap.put(void.class, voidNode); 186 | javaMap.put(Void.class, voidNode); 187 | registerPrimitiveClass(String.class, stringNode); 188 | registerPrimitiveClass(CharSequence.class, stringNode); 189 | registerPrimitiveClass(IntRange.class, new ZenIntRangeNode(this)); 190 | javaMap.put(Object.class, anyClass); 191 | } 192 | 193 | private void removeOldScripts() { 194 | try { 195 | Files.walkFileTree(ProbeZS.instance.generatedPath, new SimpleFileVisitor() { 196 | @Override 197 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 198 | String fileString = file.toString(); 199 | if (fileString.endsWith(".json") || fileString.endsWith(".dzs")) { 200 | if (!file.getParent().equals(ProbeZS.instance.generatedPath)) { 201 | Files.delete(file); 202 | } 203 | } 204 | return FileVisitResult.CONTINUE; 205 | } 206 | }); 207 | } catch (IOException e) { 208 | ProbeZS.logger.error("Failed to remove old scripts", e); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/ZenConstructorNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import youyihj.probezs.member.ExecutableData; 4 | import youyihj.probezs.member.ParameterData; 5 | import youyihj.probezs.util.IndentStringBuilder; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | /** 12 | * @author youyihj 13 | */ 14 | public class ZenConstructorNode extends ZenExecutableNode implements IHasImportMembers { 15 | private final List parameters; 16 | 17 | public ZenConstructorNode(List parameters) { 18 | this.parameters = parameters; 19 | } 20 | 21 | public static ZenConstructorNode read(ExecutableData constructor, ZenClassTree tree) { 22 | List parameterNodes = new ArrayList<>(constructor.getParameterCount()); 23 | ParameterData[] parameters = constructor.getParameters(); 24 | for (int i = 0; i < constructor.getParameterCount(); i++) { 25 | parameterNodes.add(ZenParameterNode.read(constructor, i, parameters[i], tree)); 26 | } 27 | return new ZenConstructorNode(parameterNodes); 28 | } 29 | 30 | @Override 31 | public void fillImportMembers(Set members) { 32 | for (ZenParameterNode parameter : parameters) { 33 | parameter.fillImportMembers(members); 34 | } 35 | } 36 | 37 | @Override 38 | protected boolean existed() { 39 | return true; 40 | } 41 | 42 | @Override 43 | protected void writeModifiersAndName(IndentStringBuilder sb) { 44 | sb.append("zenConstructor"); 45 | } 46 | 47 | @Override 48 | protected List getParameters() { 49 | return parameters; 50 | } 51 | 52 | @Override 53 | protected JavaTypeMirror.Result getReturnType() { 54 | return null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/ZenExecutableNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import youyihj.probezs.util.IndentStringBuilder; 4 | 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | /** 9 | * @author youyihj 10 | */ 11 | public abstract class ZenExecutableNode implements IMaybeExpansionMember, IZenDumpable { 12 | private String expansionOwner; 13 | 14 | protected abstract boolean existed(); 15 | 16 | protected abstract void writeModifiersAndName(IndentStringBuilder sb); 17 | 18 | protected abstract List getParameters(); 19 | 20 | protected abstract JavaTypeMirror.Result getReturnType(); 21 | 22 | @Override 23 | public final void toZenScript(IndentStringBuilder sb) { 24 | if (!existed()) return; 25 | if (expansionOwner != null) { 26 | sb.append("// expansion member from ").append(expansionOwner).nextLine(); 27 | } 28 | writeModifiersAndName(sb); 29 | sb.append("("); 30 | Iterator iterator = getParameters().iterator(); 31 | while (iterator.hasNext()) { 32 | iterator.next().toZenScript(sb); 33 | if (iterator.hasNext()) { 34 | sb.append(", "); 35 | } 36 | } 37 | sb.append(")"); 38 | JavaTypeMirror.Result returnType = getReturnType(); 39 | if (returnType != null) { 40 | sb.append(" as ").append(returnType.getQualifiedName()); 41 | } 42 | sb.append(";"); 43 | } 44 | 45 | @Override 46 | public void setOwner(String owner) { 47 | this.expansionOwner = owner; 48 | } 49 | 50 | @Override 51 | public String getOwner() { 52 | return expansionOwner; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/ZenMemberNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import stanhebben.zenscript.annotations.ZenMethod; 5 | import stanhebben.zenscript.annotations.ZenMethodStatic; 6 | import youyihj.probezs.member.ExecutableData; 7 | import youyihj.probezs.util.IndentStringBuilder; 8 | 9 | import java.lang.reflect.Modifier; 10 | import java.util.List; 11 | import java.util.Set; 12 | import java.util.function.Supplier; 13 | 14 | /** 15 | * @author youyihj 16 | */ 17 | public class ZenMemberNode extends ZenExecutableNode implements IZenDumpable, IHasImportMembers { 18 | private final String name; 19 | private final List parameters; 20 | private final boolean isStatic; 21 | 22 | @SerializedName("returnType") 23 | private final Supplier returnTypeResultSupplier; 24 | 25 | public ZenMemberNode(String name, Supplier returnType, List parameters, boolean isStatic) { 26 | this.name = name; 27 | this.parameters = parameters; 28 | this.isStatic = isStatic; 29 | this.returnTypeResultSupplier = returnType; 30 | } 31 | 32 | public static ZenMemberNode read(ExecutableData method, ZenClassTree tree, boolean isClass) { 33 | if (isClass && method.isAnnotationPresent(ZenMethod.class)) { 34 | String name = method.getAnnotation(ZenMethod.class).value(); 35 | if (name.isEmpty()) { 36 | name = method.getName(); 37 | } 38 | return readDirectly(method, tree, name, Modifier.isStatic(method.getModifiers()), false); 39 | } 40 | if (!isClass) { 41 | if (method.isAnnotationPresent(ZenMethod.class)) { 42 | String name = method.getAnnotation(ZenMethod.class).value(); 43 | if (name.isEmpty()) { 44 | name = method.getName(); 45 | } 46 | return readDirectly(method, tree, name, false, true); 47 | } 48 | if (method.isAnnotationPresent(ZenMethodStatic.class)) { 49 | String name = method.getAnnotation(ZenMethodStatic.class).value(); 50 | if (name.isEmpty()) { 51 | name = method.getName(); 52 | } 53 | return readDirectly(method, tree, name, true, false); 54 | } 55 | } 56 | return null; 57 | } 58 | 59 | public static ZenMemberNode readDirectly(ExecutableData method, ZenClassTree tree, String name, boolean isStatic, boolean expansion) { 60 | int startIndex = expansion ? 1 : 0; 61 | return new ZenMemberNode( 62 | name, 63 | tree.createJavaTypeMirror(method.getReturnType()), 64 | ZenParameterNode.read(method, startIndex, tree), 65 | isStatic 66 | ); 67 | } 68 | 69 | @Override 70 | protected boolean existed() { 71 | return parameters.stream().map(ZenParameterNode::getType).allMatch(JavaTypeMirror::isExisted); 72 | } 73 | 74 | @Override 75 | protected void writeModifiersAndName(IndentStringBuilder sb) { 76 | if (isStatic) { 77 | sb.append("static "); 78 | } 79 | sb.append("function ").append(name); 80 | } 81 | 82 | @Override 83 | protected List getParameters() { 84 | return parameters; 85 | } 86 | 87 | @Override 88 | protected JavaTypeMirror.Result getReturnType() { 89 | return returnTypeResultSupplier.get(); 90 | } 91 | 92 | @Override 93 | public void fillImportMembers(Set members) { 94 | for (ZenParameterNode parameter : parameters) { 95 | parameter.fillImportMembers(members); 96 | } 97 | members.addAll(returnTypeResultSupplier.get().getTypeVariables()); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/ZenOperatorNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonSerializationContext; 5 | import com.google.gson.JsonSerializer; 6 | import youyihj.probezs.util.IntersectionType; 7 | import youyihj.probezs.util.IndentStringBuilder; 8 | 9 | import java.lang.reflect.Type; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import java.util.Set; 13 | import java.util.function.Supplier; 14 | 15 | /** 16 | * @author youyihj 17 | */ 18 | public class ZenOperatorNode extends ZenExecutableNode implements IZenDumpable, IHasImportMembers { 19 | private final String name; 20 | private final List parameters; 21 | 22 | protected Supplier returnType; 23 | 24 | public ZenOperatorNode(String name, List parameters, Supplier returnTypes) { 25 | this.name = name; 26 | this.parameters = parameters; 27 | this.returnType = returnTypes; 28 | } 29 | 30 | @Override 31 | public void fillImportMembers(Set members) { 32 | for (ZenParameterNode parameter : parameters) { 33 | parameter.fillImportMembers(members); 34 | } 35 | members.addAll(returnType.get().getTypeVariables()); 36 | } 37 | 38 | @Override 39 | protected boolean existed() { 40 | return parameters.stream().map(ZenParameterNode::getType).allMatch(JavaTypeMirror::isExisted); 41 | } 42 | 43 | @Override 44 | protected void writeModifiersAndName(IndentStringBuilder sb) { 45 | sb.append("operator ").append(name); 46 | } 47 | 48 | @Override 49 | protected List getParameters() { 50 | return parameters; 51 | } 52 | 53 | @Override 54 | protected JavaTypeMirror.Result getReturnType() { 55 | return returnType.get(); 56 | } 57 | 58 | public static class As extends ZenOperatorNode { 59 | 60 | private IntersectionType intersectionType; 61 | private final ZenClassTree tree; 62 | 63 | public As(ZenClassTree tree) { 64 | super("as", Collections.emptyList(), null); 65 | this.tree = tree; 66 | } 67 | 68 | public void appendCastType(Type type) { 69 | if (intersectionType == null) { 70 | intersectionType = new IntersectionType(new Type[] {type}); 71 | } else { 72 | intersectionType = intersectionType.append(type); 73 | } 74 | returnType = tree.createJavaTypeMirror(intersectionType); 75 | } 76 | } 77 | 78 | public static class AsSerializer implements JsonSerializer { 79 | 80 | @Override 81 | public JsonElement serialize(As src, Type typeOfSrc, JsonSerializationContext context) { 82 | return context.serialize(src, ZenOperatorNode.class); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/ZenParameterNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import com.google.gson.*; 4 | import stanhebben.zenscript.annotations.Optional; 5 | import youyihj.probezs.docs.ParameterNameMappings; 6 | import youyihj.probezs.member.ExecutableData; 7 | import youyihj.probezs.member.ParameterData; 8 | import youyihj.probezs.util.IndentStringBuilder; 9 | import youyihj.probezs.util.ZenKeywords; 10 | 11 | import java.lang.reflect.Type; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Set; 15 | import java.util.function.Supplier; 16 | 17 | /** 18 | * @author youyihj 19 | */ 20 | public class ZenParameterNode implements IZenDumpable, IHasImportMembers { 21 | private final Supplier name; 22 | private final JavaTypeMirror type; 23 | private final Optional optional; 24 | private final boolean varArgs; 25 | 26 | public ZenParameterNode(Supplier name, JavaTypeMirror type, Optional optional, boolean varArgs) { 27 | this.name = name; 28 | this.type = type; 29 | this.optional = optional; 30 | this.varArgs = varArgs; 31 | } 32 | 33 | public static ZenParameterNode read(ExecutableData method, int index, ParameterData parameter, ZenClassTree tree) { 34 | boolean varArgs = parameter.isVarargs(); 35 | JavaTypeMirror returnType; 36 | if (varArgs && parameter.getType().isArray()) { 37 | returnType = tree.createJavaTypeMirror(parameter.getType().getComponentType()); 38 | } else { 39 | returnType = tree.createJavaTypeMirror(parameter.getGenericType()); 40 | } 41 | Supplier name = () -> { 42 | List list = ParameterNameMappings.find(method); 43 | if (list != null && index < list.size()) { 44 | return list.get(index); 45 | } 46 | return parameter.getName(); 47 | }; 48 | return new ZenParameterNode(name, returnType, parameter.getAnnotation(Optional.class), varArgs); 49 | } 50 | 51 | public static List read(ExecutableData method, int startIndex, ZenClassTree tree) { 52 | ParameterData[] parameters = method.getParameters(); 53 | List parameterNodes = new ArrayList<>(method.getParameterCount()); 54 | for (int i = startIndex; i < method.getParameterCount(); i++) { 55 | parameterNodes.add(ZenParameterNode.read(method, i, parameters[i], tree)); 56 | } 57 | return parameterNodes; 58 | } 59 | 60 | public String getName() { 61 | String name = this.name.get(); 62 | if (ZenKeywords.is(name)) { 63 | name = "_" + name; 64 | } 65 | if (varArgs) { 66 | name = "..." + name; 67 | } 68 | return name; 69 | } 70 | 71 | public JavaTypeMirror getType() { 72 | return type; 73 | } 74 | 75 | public Optional getOptional() { 76 | return optional; 77 | } 78 | 79 | @Override 80 | public void toZenScript(IndentStringBuilder sb) { 81 | String typeName = type.get().getQualifiedName(); 82 | sb.append(getName()).append(" as ").append(typeName); 83 | if (optional != null) { 84 | sb.append(" = "); 85 | switch (typeName) { 86 | case "int": 87 | case "short": 88 | case "long": 89 | case "byte": 90 | sb.append(String.valueOf(optional.valueLong())); 91 | break; 92 | case "bool": 93 | sb.append(String.valueOf(optional.valueBoolean())); 94 | break; 95 | case "float": 96 | case "double": 97 | sb.append(String.valueOf(optional.valueDouble())); 98 | break; 99 | case "string": 100 | if (optional.value().isEmpty()) { 101 | sb.append(null); 102 | } else { 103 | sb.append("\"").append(optional.value()).append("\""); 104 | } 105 | break; 106 | default: 107 | if (optional.methodClass() == Optional.class) { 108 | sb.append(null); 109 | } else { 110 | sb.append(optional.methodClass().getName()) 111 | .append(".") 112 | .append(optional.methodName()) 113 | .append("(") 114 | .append("\"").append(optional.value()).append("\"") 115 | .append(")"); 116 | } 117 | break; 118 | } 119 | } 120 | } 121 | 122 | @Override 123 | public void fillImportMembers(Set members) { 124 | members.addAll(type.get().getTypeVariables()); 125 | } 126 | 127 | public static class Serializer implements JsonSerializer { 128 | 129 | @Override 130 | public JsonElement serialize(ZenParameterNode src, Type typeOfSrc, JsonSerializationContext context) { 131 | JsonObject json = new JsonObject(); 132 | json.addProperty("name", src.getName()); 133 | json.add("type", context.serialize(src.getType())); 134 | Optional optional = src.getOptional(); 135 | if (optional != null) { 136 | String typeName = src.getType().get().getQualifiedName(); 137 | switch (typeName) { 138 | case "int": 139 | case "short": 140 | case "long": 141 | case "byte": 142 | json.addProperty("optional", optional.valueLong()); 143 | break; 144 | case "bool": 145 | json.addProperty("optional", optional.valueBoolean()); 146 | break; 147 | case "float": 148 | case "double": 149 | json.addProperty("optional", optional.valueDouble()); 150 | break; 151 | case "string": 152 | if (optional.value().isEmpty()) { 153 | json.add("optional", JsonNull.INSTANCE); 154 | } else { 155 | json.addProperty("optional", optional.value()); 156 | } 157 | break; 158 | default: 159 | if (optional.methodClass() == Optional.class) { 160 | json.add("optional", JsonNull.INSTANCE); 161 | } else { 162 | String sb = optional.methodClass().getName() + 163 | "." + 164 | optional.methodName() + 165 | "(" + 166 | "\"" + optional.value() + "\"" + 167 | ")"; 168 | json.addProperty("optional", sb); 169 | } 170 | break; 171 | } 172 | } 173 | return json; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/ZenPropertyNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree; 2 | 3 | import youyihj.probezs.util.IndentStringBuilder; 4 | 5 | import java.util.Set; 6 | 7 | /** 8 | * @author youyihj 9 | */ 10 | public class ZenPropertyNode implements IZenDumpable, IHasImportMembers, IMaybeExpansionMember { 11 | private final JavaTypeMirror type; 12 | private final String name; 13 | 14 | private boolean hasGetter; 15 | private boolean hasSetter; 16 | 17 | private boolean isStatic; 18 | private String owner; 19 | 20 | public ZenPropertyNode(JavaTypeMirror type, String name) { 21 | this.type = type; 22 | this.name = name; 23 | } 24 | 25 | public boolean isHasGetter() { 26 | return hasGetter; 27 | } 28 | 29 | public void setHasGetter(boolean hasGetter) { 30 | this.hasGetter = hasGetter; 31 | } 32 | 33 | public boolean isHasSetter() { 34 | return hasSetter; 35 | } 36 | 37 | public void setHasSetter(boolean hasSetter) { 38 | this.hasSetter = hasSetter; 39 | } 40 | 41 | public boolean isStatic() { 42 | return isStatic; 43 | } 44 | 45 | public void setStatic(boolean aStatic) { 46 | isStatic = aStatic; 47 | } 48 | 49 | @Override 50 | public void toZenScript(IndentStringBuilder sb) { 51 | if (type.isExisted()) { 52 | if (owner != null) { 53 | sb.append("// expansion member from ").append(owner).nextLine(); 54 | } 55 | String declareKeyword = isStatic ? "static" : isHasSetter() ? "var" : "val"; 56 | sb.append(declareKeyword); 57 | sb.append(" ").append(name).append(" as ").append(type.get().getQualifiedName()).append(";"); 58 | } 59 | } 60 | 61 | @Override 62 | public void fillImportMembers(Set members) { 63 | members.addAll(type.get().getTypeVariables()); 64 | } 65 | 66 | 67 | @Override 68 | public void setOwner(String owner) { 69 | this.owner = owner; 70 | } 71 | 72 | @Override 73 | public String getOwner() { 74 | return owner; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/global/ZenGlobalFieldNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.global; 2 | 3 | import youyihj.probezs.tree.IHasImportMembers; 4 | import youyihj.probezs.tree.IZenDumpable; 5 | import youyihj.probezs.tree.JavaTypeMirror; 6 | import youyihj.probezs.tree.ZenClassNode; 7 | import youyihj.probezs.util.IndentStringBuilder; 8 | 9 | import java.util.Set; 10 | 11 | /** 12 | * @author youyihj 13 | */ 14 | public class ZenGlobalFieldNode implements IZenDumpable, IHasImportMembers, Comparable { 15 | private final String name; 16 | private final JavaTypeMirror type; 17 | 18 | public ZenGlobalFieldNode(String name, JavaTypeMirror type) { 19 | this.name = name; 20 | this.type = type; 21 | } 22 | 23 | @Override 24 | public void toZenScript(IndentStringBuilder sb) { 25 | sb.append("global ").append(name).append(" as ").append(type.get().getQualifiedName()).append(";"); 26 | } 27 | 28 | @Override 29 | public void fillImportMembers(Set members) { 30 | members.addAll(type.get().getTypeVariables()); 31 | } 32 | 33 | @Override 34 | public int compareTo(ZenGlobalFieldNode o) { 35 | return name.compareTo(o.name); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/global/ZenGlobalMemberTree.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.global; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonPrimitive; 6 | import com.google.gson.reflect.TypeToken; 7 | import crafttweaker.mc1120.util.CraftTweakerHacks; 8 | import stanhebben.zenscript.symbols.IZenSymbol; 9 | import stanhebben.zenscript.symbols.SymbolJavaStaticField; 10 | import stanhebben.zenscript.symbols.SymbolJavaStaticGetter; 11 | import stanhebben.zenscript.symbols.SymbolJavaStaticMethod; 12 | import stanhebben.zenscript.type.natives.IJavaMethod; 13 | import stanhebben.zenscript.type.natives.JavaMethod; 14 | import youyihj.probezs.ProbeZS; 15 | import youyihj.probezs.ProbeZSConfig; 16 | import youyihj.probezs.tree.IZenDumpable; 17 | import youyihj.probezs.tree.ZenClassNode; 18 | import youyihj.probezs.tree.ZenClassTree; 19 | import youyihj.probezs.tree.primitive.IPrimitiveType; 20 | import youyihj.probezs.util.FileUtils; 21 | import youyihj.probezs.util.IndentStringBuilder; 22 | 23 | import java.io.IOException; 24 | import java.lang.reflect.Field; 25 | import java.util.Map; 26 | import java.util.Set; 27 | import java.util.TreeSet; 28 | 29 | /** 30 | * @author youyihj 31 | */ 32 | public class ZenGlobalMemberTree { 33 | private final ZenClassTree tree; 34 | private final Set fields = new TreeSet<>(); 35 | private final Set members = new TreeSet<>(); 36 | 37 | public ZenGlobalMemberTree(ZenClassTree tree) { 38 | this.tree = tree; 39 | } 40 | 41 | public void readGlobals(Map globalMap) { 42 | globalMap.forEach((name, symbol) -> { 43 | if (symbol instanceof SymbolJavaStaticField) { 44 | SymbolJavaStaticField javaStaticField = (SymbolJavaStaticField) symbol; 45 | Field field = CraftTweakerHacks.getPrivateObject(javaStaticField, "field"); 46 | tree.putGlobalInternalClass(field.getType()); 47 | fields.add(new ZenGlobalFieldNode(name, tree.createJavaTypeMirror(field.getGenericType()))); 48 | } else if (symbol instanceof SymbolJavaStaticGetter) { 49 | SymbolJavaStaticGetter javaStaticGetter = (SymbolJavaStaticGetter) symbol; 50 | IJavaMethod method = CraftTweakerHacks.getPrivateObject(javaStaticGetter, "method"); 51 | tree.putGlobalInternalClass(method.getReturnType().toJavaClass()); 52 | fields.add(new ZenGlobalFieldNode(name, tree.createJavaTypeMirror(method.getReturnType().toJavaClass()))); 53 | } else if (symbol instanceof SymbolJavaStaticMethod) { 54 | SymbolJavaStaticMethod javaStaticMethod = (SymbolJavaStaticMethod) symbol; 55 | IJavaMethod javaMethod = CraftTweakerHacks.getPrivateObject(javaStaticMethod, "method"); 56 | if (javaMethod instanceof JavaMethod) { 57 | members.add(ZenGlobalMethodNode.read(name, ProbeZS.getMemberFactory().reflect(((JavaMethod) javaMethod).getMethod()), tree)); 58 | } 59 | } 60 | }); 61 | } 62 | 63 | public Set getImportMembers() { 64 | Set imports = new TreeSet() { 65 | @Override 66 | public boolean add(ZenClassNode node) { 67 | if (node instanceof IPrimitiveType) { 68 | return false; 69 | } else { 70 | return super.add(node); 71 | } 72 | } 73 | }; 74 | for (ZenGlobalFieldNode field : fields) { 75 | field.fillImportMembers(imports); 76 | } 77 | for (ZenGlobalMethodNode member : members) { 78 | member.fillImportMembers(imports); 79 | } 80 | return imports; 81 | } 82 | 83 | public void output() { 84 | if (ProbeZSConfig.dumpDZS) { 85 | outputDZS(); 86 | } 87 | if (ProbeZSConfig.dumpJson) { 88 | outputJson(); 89 | } 90 | } 91 | 92 | private void outputJson() { 93 | JsonObject json = new JsonObject(); 94 | JsonArray imports = new JsonArray(); 95 | for (ZenClassNode importMember : getImportMembers()) { 96 | imports.add(new JsonPrimitive(importMember.getName())); 97 | } 98 | json.add("imports", imports); 99 | json.add("fields", ZenClassTree.GSON.toJsonTree(fields, new TypeToken>() {}.getType())); 100 | json.add("members", ZenClassTree.GSON.toJsonTree(members, new TypeToken>() {}.getType())); 101 | try { 102 | FileUtils.createFile(ProbeZS.instance.generatedPath.resolve("globals.json"), ZenClassTree.GSON.toJson(json)); 103 | } catch (IOException e) { 104 | ProbeZS.logger.error("Failed output globals dzs", e); 105 | } 106 | } 107 | 108 | private void outputDZS() { 109 | IndentStringBuilder sb = new IndentStringBuilder(); 110 | for (ZenClassNode anImport : getImportMembers()) { 111 | sb.append("import ").append(anImport.getName()).append(";").nextLine(); 112 | } 113 | sb.interLine(); 114 | for (IZenDumpable field : fields) { 115 | field.toZenScript(sb); 116 | sb.nextLine(); 117 | } 118 | sb.nextLine(); 119 | for (ZenGlobalMethodNode member : members) { 120 | member.toZenScript(sb); 121 | sb.nextLine(); 122 | } 123 | try { 124 | FileUtils.createFile(ProbeZS.instance.generatedPath.resolve("globals.dzs"), sb.toString()); 125 | } catch (IOException e) { 126 | ProbeZS.logger.error("Failed output globals dzs", e); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/global/ZenGlobalMethodNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.global; 2 | 3 | import youyihj.probezs.ProbeZS; 4 | import youyihj.probezs.ProbeZSConfig; 5 | import youyihj.probezs.member.ExecutableData; 6 | import youyihj.probezs.member.ParameterData; 7 | import youyihj.probezs.tree.*; 8 | import youyihj.probezs.util.IndentStringBuilder; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Set; 13 | 14 | /** 15 | * @author youyihj 16 | */ 17 | public class ZenGlobalMethodNode extends ZenExecutableNode implements IZenDumpable, IHasImportMembers, Comparable { 18 | private final String name; 19 | private final JavaTypeMirror returnType; 20 | private final List parameters; 21 | 22 | public ZenGlobalMethodNode(String name, JavaTypeMirror returnType, List parameters) { 23 | this.name = name; 24 | this.returnType = returnType; 25 | this.parameters = parameters; 26 | } 27 | 28 | public static ZenGlobalMethodNode read(String name, ExecutableData method, ZenClassTree tree) { 29 | JavaTypeMirror returnType = tree.createJavaTypeMirror(method.getReturnType()); 30 | 31 | ParameterData[] parameters = method.getParameters(); 32 | List parameterNodes = new ArrayList<>(method.getParameterCount()); 33 | for (int i = 0; i < method.getParameterCount(); i++) { 34 | parameterNodes.add(ZenParameterNode.read(method, i, parameters[i], tree)); 35 | } 36 | ZenGlobalMethodNode globalMethodNode = new ZenGlobalMethodNode(name, returnType, parameterNodes); 37 | if (ProbeZSConfig.outputSourceExpansionMembers) { 38 | String classOwner = ProbeZS.instance.getClassOwner(method.getDecalredClass()); 39 | if (!"crafttweaker".equals(classOwner)) { 40 | globalMethodNode.setOwner(classOwner); 41 | } 42 | } 43 | return globalMethodNode; 44 | } 45 | 46 | @Override 47 | public void fillImportMembers(Set members) { 48 | members.addAll(returnType.get().getTypeVariables()); 49 | for (ZenParameterNode parameter : parameters) { 50 | parameter.fillImportMembers(members); 51 | } 52 | } 53 | 54 | @Override 55 | public int compareTo(ZenGlobalMethodNode o) { 56 | return name.compareTo(o.name); 57 | } 58 | 59 | @Override 60 | protected boolean existed() { 61 | return true; 62 | } 63 | 64 | @Override 65 | protected void writeModifiersAndName(IndentStringBuilder sb) { 66 | sb.append("global function ").append(name); 67 | } 68 | 69 | @Override 70 | protected List getParameters() { 71 | return parameters; 72 | } 73 | 74 | @Override 75 | protected JavaTypeMirror.Result getReturnType() { 76 | return returnType.get(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/IPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | /** 4 | * @author youyihj 5 | */ 6 | public interface IPrimitiveType { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenAnyNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenAnyNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenAnyNode(ZenClassTree tree) { 11 | super("any", tree); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenBoolNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenBoolNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenBoolNode(ZenClassTree tree) { 11 | super("bool", tree); 12 | // casterClasses.add(tree.createLazyClassNode(String.class)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenByteNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenByteNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenByteNode(ZenClassTree tree) { 11 | super("byte", tree); 12 | // casterClasses.add(tree.createLazyClassNode(short.class)); 13 | // casterClasses.add(tree.createLazyClassNode(int.class)); 14 | // casterClasses.add(tree.createLazyClassNode(long.class)); 15 | // casterClasses.add(tree.createLazyClassNode(float.class)); 16 | // casterClasses.add(tree.createLazyClassNode(double.class)); 17 | // casterClasses.add(tree.createLazyClassNode(String.class)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenDoubleNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenDoubleNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenDoubleNode(ZenClassTree tree) { 11 | super("double", tree); 12 | // casterClasses.add(tree.createLazyClassNode(byte.class)); 13 | // casterClasses.add(tree.createLazyClassNode(short.class)); 14 | // casterClasses.add(tree.createLazyClassNode(int.class)); 15 | // casterClasses.add(tree.createLazyClassNode(long.class)); 16 | // casterClasses.add(tree.createLazyClassNode(float.class)); 17 | // casterClasses.add(tree.createLazyClassNode(String.class)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenFloatNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenFloatNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenFloatNode(ZenClassTree tree) { 11 | super("float", tree); 12 | // casterClasses.add(tree.createLazyClassNode(byte.class)); 13 | // casterClasses.add(tree.createLazyClassNode(short.class)); 14 | // casterClasses.add(tree.createLazyClassNode(int.class)); 15 | // casterClasses.add(tree.createLazyClassNode(long.class)); 16 | // casterClasses.add(tree.createLazyClassNode(double.class)); 17 | // casterClasses.add(tree.createLazyClassNode(String.class)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenIntNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenIntNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenIntNode(ZenClassTree tree) { 11 | super("int", tree); 12 | // casterClasses.add(tree.createLazyClassNode(byte.class)); 13 | // casterClasses.add(tree.createLazyClassNode(short.class)); 14 | // casterClasses.add(tree.createLazyClassNode(long.class)); 15 | // casterClasses.add(tree.createLazyClassNode(float.class)); 16 | // casterClasses.add(tree.createLazyClassNode(double.class)); 17 | // casterClasses.add(tree.createLazyClassNode(String.class)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenIntRangeNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import com.google.gson.reflect.TypeToken; 4 | import stanhebben.zenscript.type.ZenTypeIntRange; 5 | import youyihj.probezs.tree.*; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** 11 | * @author youyihj 12 | */ 13 | public class ZenIntRangeNode extends ZenClassNode { 14 | public ZenIntRangeNode(ZenClassTree tree) { 15 | super(ZenTypeIntRange.INTRANGE.getName(), tree); 16 | properties.put("from", new ZenPropertyNode(tree.createJavaTypeMirror(int.class), "from")); 17 | properties.put("to", new ZenPropertyNode(tree.createJavaTypeMirror(int.class), "to")); 18 | operators.put("for_in", new ZenOperatorNode( 19 | "for_in", 20 | Collections.emptyList(), 21 | tree.createJavaTypeMirror(new TypeToken>() {}.getType()) 22 | )); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenLongNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenLongNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenLongNode(ZenClassTree tree) { 11 | super("long", tree); 12 | // casterClasses.add(tree.createLazyClassNode(byte.class)); 13 | // casterClasses.add(tree.createLazyClassNode(short.class)); 14 | // casterClasses.add(tree.createLazyClassNode(int.class)); 15 | // casterClasses.add(tree.createLazyClassNode(float.class)); 16 | // casterClasses.add(tree.createLazyClassNode(double.class)); 17 | // casterClasses.add(tree.createLazyClassNode(String.class)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenShortNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenShortNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenShortNode(ZenClassTree tree) { 11 | super("short", tree); 12 | // casterClasses.add(tree.createLazyClassNode(byte.class)); 13 | // casterClasses.add(tree.createLazyClassNode(int.class)); 14 | // casterClasses.add(tree.createLazyClassNode(long.class)); 15 | // casterClasses.add(tree.createLazyClassNode(float.class)); 16 | // casterClasses.add(tree.createLazyClassNode(double.class)); 17 | // casterClasses.add(tree.createLazyClassNode(String.class)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenStringNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.ProbeZS; 4 | import youyihj.probezs.tree.ZenClassNode; 5 | import youyihj.probezs.tree.ZenClassTree; 6 | import youyihj.probezs.tree.ZenMemberNode; 7 | import youyihj.probezs.tree.ZenParameterNode; 8 | 9 | import java.lang.reflect.Method; 10 | import java.lang.reflect.Modifier; 11 | import java.lang.reflect.Parameter; 12 | import java.lang.reflect.Type; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * @author youyihj 18 | */ 19 | public class ZenStringNode extends ZenClassNode implements IPrimitiveType { 20 | public ZenStringNode(ZenClassTree tree) { 21 | super("string", tree); 22 | // casterClasses.add(tree.createLazyClassNode(boolean.class)); 23 | // casterClasses.add(tree.createLazyClassNode(byte.class)); 24 | // casterClasses.add(tree.createLazyClassNode(short.class)); 25 | // casterClasses.add(tree.createLazyClassNode(long.class)); 26 | // casterClasses.add(tree.createLazyClassNode(float.class)); 27 | // casterClasses.add(tree.createLazyClassNode(double.class)); 28 | readJavaMethods(tree); 29 | } 30 | 31 | private void readJavaMethods(ZenClassTree tree) { 32 | try { 33 | readMethod: 34 | for (Method method : String.class.getDeclaredMethods()) { 35 | if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) { 36 | List parameterNodes = new ArrayList<>(method.getParameterCount()); 37 | Parameter[] parameters = method.getParameters(); 38 | if (convertType(method.getGenericReturnType()) == null) continue; 39 | for (int i = 0; i < method.getParameterCount(); i++) { 40 | Parameter parameter = parameters[i]; 41 | Type convertType = convertType(parameter.getParameterizedType()); 42 | if (convertType == null) continue readMethod; 43 | parameterNodes.add(ZenParameterNode.read(ProbeZS.getMemberFactory().reflect(method), i, ProbeZS.getMemberFactory().reflect(parameter), tree)); 44 | } 45 | ZenMemberNode zenMemberNode = new ZenMemberNode(method.getName(), tree.createJavaTypeMirror(method.getGenericReturnType()), parameterNodes, false); 46 | members.add(zenMemberNode); 47 | } 48 | } 49 | } catch (Exception e) { 50 | throw new RuntimeException(e); 51 | } 52 | } 53 | 54 | private Type convertType(Type type) { 55 | if (type instanceof Class) { 56 | Class clazz = (Class) type; 57 | if (clazz.isPrimitive() && clazz != char.class) { 58 | return clazz; 59 | } 60 | if (clazz == CharSequence.class || clazz == String.class) { 61 | return String.class; 62 | } 63 | return null; 64 | } 65 | return type; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/tree/primitive/ZenVoidNode.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.tree.primitive; 2 | 3 | import youyihj.probezs.tree.ZenClassNode; 4 | import youyihj.probezs.tree.ZenClassTree; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class ZenVoidNode extends ZenClassNode implements IPrimitiveType { 10 | public ZenVoidNode(ZenClassTree tree) { 11 | super("void", tree); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/util/Arrays.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.util; 2 | 3 | import java.lang.reflect.Array; 4 | import java.util.function.Function; 5 | 6 | /** 7 | * @author youyihj 8 | */ 9 | public class Arrays { 10 | public static U[] map(T[] array, Class resultClass, Function mapper) { 11 | @SuppressWarnings("unchecked") 12 | U[] result = ((U[]) Array.newInstance(resultClass, array.length)); 13 | for (int i = 0; i < array.length; i++) { 14 | result[i] = mapper.apply(array[i]); 15 | } 16 | return result; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/util/DebugAPIAdapter.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.util; 2 | 3 | import stanhebben.zenscript.annotations.ZenGetter; 4 | import stanhebben.zenscript.annotations.ZenProperty; 5 | import stanhebben.zenscript.util.ZenTypeUtil; 6 | 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Method; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.Optional; 13 | import java.util.stream.StreamSupport; 14 | 15 | @SuppressWarnings("unused") 16 | public class DebugAPIAdapter { 17 | 18 | 19 | public static void init() { 20 | // DO nothing, only make sure that class is loaded 21 | } 22 | 23 | public static Object[] iterableToArray(Iterable iterable) { 24 | return StreamSupport.stream(iterable.spliterator(), false).toArray(); 25 | } 26 | 27 | public static String[] memberSignatures(Class clazz) { 28 | List result = new ArrayList<>(); 29 | for (Field field : clazz.getFields()) { 30 | Optional annotation = Arrays.stream(field.getAnnotations()).filter(it -> it instanceof ZenProperty).map(it -> (ZenProperty) it).findAny(); 31 | if (!annotation.isPresent()) { 32 | continue; 33 | } 34 | String propertyName = annotation.get().value(); 35 | if (propertyName.isEmpty()) { 36 | propertyName = field.getName(); 37 | } 38 | result.add(propertyName + ":" + field.getName()); 39 | } 40 | 41 | for (Method method : clazz.getMethods()) { 42 | Optional annotation = Arrays.stream(method.getAnnotations()).filter(it -> it instanceof ZenGetter).map(it -> (ZenGetter) it).findAny(); 43 | if (!annotation.isPresent()) { 44 | continue; 45 | } 46 | 47 | String propertyName = annotation.get().value(); 48 | if (propertyName.isEmpty()) { 49 | propertyName = method.getName(); 50 | } 51 | result.add(propertyName + ":" + method.getName() + ":" + ZenTypeUtil.descriptor(method)); 52 | 53 | } 54 | 55 | return result.toArray(new String[0]); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/util/FileUtils.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.util; 2 | 3 | import java.io.IOException; 4 | import java.nio.charset.StandardCharsets; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.StandardOpenOption; 8 | import java.util.Collections; 9 | 10 | /** 11 | * @author youyihj 12 | */ 13 | public class FileUtils { 14 | public static void createFile(Path path, String content) throws IOException { 15 | Files.createDirectories(path.getParent()); 16 | Files.write(path, Collections.singleton(content), StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/util/IndentStringBuilder.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.util; 2 | 3 | /** 4 | * @author youyihj 5 | */ 6 | @SuppressWarnings("UnusedReturnValue") 7 | public class IndentStringBuilder { 8 | private int indentLevel; 9 | private final int indentSpaceCount; 10 | private final StringBuilder sb = new StringBuilder(); 11 | 12 | private boolean emptyLine = true; 13 | 14 | public IndentStringBuilder() { 15 | this(4); 16 | } 17 | 18 | public IndentStringBuilder(int indentSpaceCount) { 19 | this.indentSpaceCount = indentSpaceCount; 20 | } 21 | 22 | public IndentStringBuilder append(String s) { 23 | sb.append(s); 24 | emptyLine = false; 25 | return this; 26 | } 27 | 28 | public IndentStringBuilder nextLine() { 29 | if (!emptyLine) { 30 | forceNextLine(); 31 | } 32 | return this; 33 | } 34 | 35 | public IndentStringBuilder forceNextLine() { 36 | sb.append(System.lineSeparator()); 37 | int spaceCount = indentSpaceCount * indentLevel; 38 | for (int i = 0; i < spaceCount; i++) { 39 | sb.append(' '); 40 | } 41 | emptyLine = true; 42 | return this; 43 | } 44 | 45 | public IndentStringBuilder interLine() { 46 | return nextLine().forceNextLine(); 47 | } 48 | 49 | public IndentStringBuilder push() { 50 | indentLevel++; 51 | forceNextLine(); 52 | return this; 53 | } 54 | 55 | public IndentStringBuilder pop() { 56 | indentLevel--; 57 | forceNextLine(); 58 | return this; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return sb.toString(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/util/IntersectionType.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.util; 2 | 3 | import org.apache.commons.lang3.ArrayUtils; 4 | 5 | import java.lang.reflect.Type; 6 | import java.util.Arrays; 7 | import java.util.Objects; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * @author youyihj 12 | */ 13 | public class IntersectionType implements Type { 14 | private final Type[] types; 15 | 16 | public IntersectionType(Type[] types) { 17 | this.types = types; 18 | } 19 | 20 | public Type[] getCompoundTypes() { 21 | return types; 22 | } 23 | 24 | public IntersectionType append(Type type) { 25 | return new IntersectionType(ArrayUtils.add(types, type)); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return Arrays.stream(getCompoundTypes()).map(Objects::toString).collect(Collectors.joining(" & ")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/util/LoadingObject.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.util; 2 | 3 | import youyihj.probezs.ProbeZS; 4 | 5 | /** 6 | * @author youyihj 7 | */ 8 | public class LoadingObject { 9 | private T value; 10 | private boolean alreadyLoaded; 11 | 12 | private LoadingObject(T value) { 13 | this.value = value; 14 | } 15 | 16 | public static LoadingObject of(T value) { 17 | LoadingObject object = new LoadingObject<>(value); 18 | ProbeZS.addLoadingObject(object); 19 | return object; 20 | } 21 | 22 | public T get() { 23 | if (alreadyLoaded) { 24 | throw new IllegalStateException("Only accessible while game loading."); 25 | } 26 | return value; 27 | } 28 | 29 | public void set(T value) { 30 | if (alreadyLoaded) { 31 | throw new IllegalStateException("Only accessible while game loading."); 32 | } 33 | this.value = value; 34 | } 35 | 36 | public void setAlreadyLoaded() { 37 | alreadyLoaded = true; 38 | this.value = null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/util/ZenKeywords.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.util; 2 | 3 | import crafttweaker.mc1120.util.CraftTweakerHacks; 4 | import stanhebben.zenscript.ZenTokener; 5 | 6 | import java.util.HashMap; 7 | import java.util.Set; 8 | 9 | /** 10 | * @author youyihj 11 | */ 12 | public class ZenKeywords { 13 | private static final Set KEYWORDS; 14 | 15 | static { 16 | HashMap tokenKeywords = CraftTweakerHacks.getPrivateStaticObject(ZenTokener.class, "KEYWORDS"); 17 | KEYWORDS = tokenKeywords.keySet(); 18 | } 19 | 20 | public static boolean is(String s) { 21 | return KEYWORDS.contains(s); 22 | } 23 | 24 | public static Set get() { 25 | return KEYWORDS; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/youyihj/probezs/util/ZenOperators.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs.util; 2 | 3 | import stanhebben.zenscript.annotations.OperatorType; 4 | 5 | import java.util.EnumMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author youyihj 10 | */ 11 | public class ZenOperators { 12 | private static final Map OPERATORS = new EnumMap<>(OperatorType.class); 13 | 14 | static { 15 | OPERATORS.put(OperatorType.ADD, "+"); 16 | OPERATORS.put(OperatorType.SUB, "-"); 17 | OPERATORS.put(OperatorType.MUL, "*"); 18 | OPERATORS.put(OperatorType.DIV, "/"); 19 | OPERATORS.put(OperatorType.MOD, "%"); 20 | OPERATORS.put(OperatorType.CAT, "~"); 21 | OPERATORS.put(OperatorType.OR, "|"); 22 | OPERATORS.put(OperatorType.AND, "&"); 23 | OPERATORS.put(OperatorType.XOR, "^"); 24 | OPERATORS.put(OperatorType.NEG, "-"); 25 | OPERATORS.put(OperatorType.NOT, "!"); 26 | OPERATORS.put(OperatorType.INDEXGET, "[]"); 27 | OPERATORS.put(OperatorType.INDEXSET, "[]="); 28 | OPERATORS.put(OperatorType.RANGE, ".."); 29 | OPERATORS.put(OperatorType.CONTAINS, "has"); 30 | OPERATORS.put(OperatorType.COMPARE, "compare"); 31 | OPERATORS.put(OperatorType.MEMBERGETTER, "."); 32 | OPERATORS.put(OperatorType.MEMBERSETTER, ".="); 33 | OPERATORS.put(OperatorType.EQUALS, "=="); 34 | } 35 | 36 | public static String getZenScriptFormat(OperatorType operatorType) { 37 | return OPERATORS.get(operatorType); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/resources/ProbeZS_at.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.crash.CrashReportCategory field_85077_c # children 2 | public net.minecraft.crash.CrashReportCategory$Entry 3 | -------------------------------------------------------------------------------- /src/main/resources/assets/base/models/item/wrench.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "base:items/wrench" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/mappings/bracket-return-types.yaml: -------------------------------------------------------------------------------- 1 | crafttweaker.mc1120.brackets.BracketHandlerBiome: crafttweaker.api.world.IBiome 2 | codersafterdark.compatskills.common.compats.reskillable.brackethandlers.BracketHandlerSkill: codersafterdark.compatskills.common.compats.reskillable.playerexpansion.wrapper.CTSkill 3 | codersafterdark.compatskills.common.compats.reskillable.brackethandlers.BracketHandlerUnlockable: codersafterdark.compatskills.common.compats.reskillable.playerexpansion.wrapper.CTUnlockable 4 | gregtech.integration.crafttweaker.material.MaterialBracketHandler: gregtech.api.unification.material.Material 5 | gregtech.integration.crafttweaker.recipe.MetaItemBracketHandler: crafttweaker.api.item.IItemStack 6 | gregtech.integration.crafttweaker.recipe.MetaTileEntityBracketHandler: crafttweaker.api.item.IItemStack 7 | gregtech.integration.crafttweaker.recipe.RecipeMapBracketHandler: gregtech.api.recipes.RecipeMap 8 | -------------------------------------------------------------------------------- /src/main/resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "probezs", 4 | "name": "ProbeZS", 5 | "description": "Export zenmethods to a .d.zs file for language server", 6 | "version": "${version}", 7 | "mcversion": "${mcversion}", 8 | "url": "", 9 | "updateUrl": "", 10 | "authorList": ["youyihj"], 11 | "credits": "", 12 | "logoFile": "", 13 | "screenshots": [], 14 | "dependencies": [] 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /src/main/resources/mixins.probezs.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "target": "@env(DEFAULT)", 4 | "minVersion": "0.8", 5 | "package": "youyihj.probezs.core.mixin", 6 | "refmap": "mixins.probezs.refmap.json", 7 | "compatibilityLevel": "JAVA_8", 8 | "mixins": [ 9 | "MixinEnvironmentMethod", 10 | "MixinMethodOutput", 11 | "MixinParsedFunction", 12 | "MixinParsedZenClass", 13 | "MixinStatementBlockOrForeach", 14 | "SymbolArgumentAccessor" 15 | ] 16 | } -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "examplemod resources", 4 | "pack_format": 3, 5 | "_comment": "A pack_format of 3 should be used starting with Minecraft 1.11. All resources, including language files, should be lowercase (eg: en_us.lang). A pack_format of 2 will load your mod resources with LegacyV2Adapter, which requires language files to have uppercase letters (eg: en_US.lang)." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/youyihj/probezs/TypeResolverTest.java: -------------------------------------------------------------------------------- 1 | package youyihj.probezs; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import youyihj.probezs.member.asm.ASMMemberFactory; 7 | import youyihj.probezs.member.asm.TypeResolver; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | /** 13 | * @author youyihj 14 | */ 15 | public class TypeResolverTest { 16 | private TypeResolver typeResolver; 17 | 18 | private static final String OBJECT = "Ljava/lang/Object;"; 19 | private static final String LIST = "Ljava/util/List;"; 20 | private static final String MAP = "Ljava/util/Map;"; 21 | private static final String INT = "I"; 22 | private static final String INT_ARRAY = "[I"; 23 | private static final String MAP_ARRAY = "[" + MAP; 24 | private static final String LIST_ELEMENT_ARRAY = "Ljava/util/List<[I>;"; 25 | private static final String LIST_GENERIC = "Ljava/util/List;"; 26 | private static final String GENERIC = "TF;"; 27 | 28 | public static String buildMethodSignature(String... paramSignature) { 29 | StringBuilder sb = new StringBuilder("("); 30 | for (String s : paramSignature) { 31 | sb.append(s); 32 | } 33 | return sb.append(")V").toString(); 34 | } 35 | 36 | public void assertResolve(String... paramSignature) { 37 | List result = typeResolver.resolveMethodArguments(buildMethodSignature(paramSignature)); 38 | Assertions.assertArrayEquals(result.toArray(new String[0]), paramSignature); 39 | } 40 | 41 | @BeforeEach 42 | public void setupTypeResolver() { 43 | typeResolver = new TypeResolver(new ASMMemberFactory(Collections.emptySet(), () -> TypeResolverTest.class::getClassLoader)); 44 | } 45 | 46 | @Test 47 | public void allPrimitiveTypes() { 48 | assertResolve("I", "I", "I", "Z", "Z", "F", "D"); 49 | } 50 | 51 | @Test 52 | public void compoundTypes() { 53 | assertResolve(OBJECT, LIST, MAP); 54 | } 55 | 56 | @Test 57 | public void primitiveAndCompoundTypes() { 58 | assertResolve(INT, OBJECT, INT, LIST, INT, MAP); 59 | } 60 | 61 | @Test 62 | public void primitiveArrays() { 63 | assertResolve(INT_ARRAY, INT_ARRAY, INT); 64 | } 65 | 66 | @Test 67 | public void primitiveArraysAndCompoundType() { 68 | assertResolve(INT_ARRAY, OBJECT, INT_ARRAY, MAP); 69 | } 70 | 71 | @Test 72 | public void compoundTypeArray() { 73 | assertResolve(MAP_ARRAY, INT, INT_ARRAY); 74 | } 75 | 76 | @Test 77 | public void compoundListElement() { 78 | assertResolve(MAP_ARRAY, LIST_ELEMENT_ARRAY, INT, INT_ARRAY); 79 | } 80 | 81 | @Test 82 | public void genericType() { 83 | assertResolve(GENERIC, LIST_GENERIC); 84 | } 85 | 86 | } 87 | --------------------------------------------------------------------------------