├── .travis.yml ├── LICENSE.TXT ├── README.adoc ├── build.gradle ├── gradle.properties ├── gradle ├── artifactory.gradle ├── bintray.gradle ├── compile.gradle ├── credentials.gradle ├── publishing.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── groovy │ └── groovyx │ │ └── ast │ │ └── bytecode │ │ ├── Bytecode.groovy │ │ ├── BytecodeASTTransformation.groovy │ │ ├── ExpressionEvaluator.java │ │ └── Instructions.groovy └── resources │ └── bytecodeTransform.gdsl └── test └── groovy ├── Helper.groovy └── groovyx └── ast └── bytecode ├── BytecodeSpock.groovy ├── FieldsBytecodeSpock.groovy ├── IndyBytecodeSpock.groovy ├── InstructionsImplementedSpock.groovy ├── PrimitiveTypesBytecodeSpock.groovy └── TypeCastingBytecodeSpock.groovy /.travis.yml: -------------------------------------------------------------------------------- 1 | script: ./gradlew -s -PbuildInfo.build.number=$TRAVIS_BUILD_NUMBER -PbuildInfo.buildUrl=https://travis-ci.org/${TRAVIS_REPO_SLUG}/builds/${TRAVIS_JOB_ID} 2 | -PbuildInfo.buildAgent.name=$USER -PbuildInfo.principal=$USER --no-daemon -Dscan clean build artifactoryPublish 3 | jdk: 4 | - oraclejdk8 5 | env: 6 | global: 7 | - secure: CwkQDwxTSEYSRoWP5MLHRnjskHdI5oZKMaYYZeQ83wNnAjUd5JpNErnOH7H7keyxWwUqzX2ywfSpQAUs3b0zpPxUVKFxqgSD6og/e9QZ2uI+9qkxRG6ntNZ56WFxNu6s4F3c3wMy3AC6/6El2ebgg58QuS4wGCfU8EXvxV/LUb8= 8 | - secure: L/IblBzEflVqLbWlaCBFU0Z+SXbZ46oUppVCq5eE9WhbbrPmW3/djvz9Ng+YH/kdHLVjkX2Z4Ud6wjUwBohPO/7E3clDmptkqndjdgxdZLt2uBqgVaEvaY+aquy1zJ1Oi5b1DCaNMOqwFyNom4ARZ5wqsdiYyGUhR/fobp/7k7E= 9 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = @Bytecode AST transformation 2 | 3 | image:https://travis-ci.org/melix/groovy-bytecode-ast.svg["Build Status", link="https://travis-ci.org/melix/groovy-bytecode-ast"] 4 | 5 | This project adds a `@Bytecode` AST transformation to Groovy, which will let you write bytecode directly as a method body! 6 | 7 | This is for *educational purposes only*. I would *never* use this in real production code. 8 | 9 | = Usage 10 | == Build script 11 | 12 | If you use Gradle: 13 | 14 | .build.gradle 15 | [source,groovy] 16 | ---- 17 | repositories { 18 | maven { url 'http://oss.jfrog.org/oss-release-local' } 19 | } 20 | dependencies { 21 | compile 'me.champeau.groovy:bytecode-xform:0.2.0' 22 | } 23 | ---- 24 | 25 | Note that you might have to use a weird classloading trick like the one https://github.com/melix/lecharny-challenge/commit/fc7b97b763b07c23b4c03a5d9796fc1129da398e[here] 26 | if you face a classloader constraint violation. 27 | 28 | But if you really insist on using Maven: 29 | 30 | .pom.xml 31 | [source,xml] 32 | ---- 33 | 34 | 35 | me.champeau.groovy 36 | bytecode-xform 37 | 0.2.0 38 | 39 | 40 | 41 | 42 | jfrog 43 | http://oss.jfrog.org/oss-release-local 44 | 45 | true 46 | 47 | 48 | false 49 | 50 | 51 | 52 | ---- 53 | 54 | == Code 55 | 56 | Then you can start using it in your Groovy code like in this example: 57 | 58 | [source,groovy] 59 | ---- 60 | 61 | @groovyx.ast.bytecode.Bytecode 62 | int fib(int n) { 63 | l0: 64 | iload 1 65 | iconst_2 66 | if_icmpge l1 67 | iload 1 68 | _goto l2 69 | l1: 70 | aload 0 71 | iload 1 72 | iconst_2 73 | isub 74 | invokevirtual '.fib','(I)I' 75 | aload 0 76 | iload 1 77 | iconst_1 78 | isub 79 | invokevirtual '.fib', '(I)I' 80 | iadd 81 | l2: 82 | ireturn 83 | } 84 | ---- 85 | 86 | A detailed explanation of the concept and why it was done can be found on some blog posts I wrote: 87 | 88 | * http://melix.github.io/blog/2011/01/16/groovy_bytecode_ast_transformation_released.html[Groovy @Bytecode AST transformation released] 89 | * http://melix.github.io/blog/2011/01/31/inline_assembly_with_groovy_evil.html[Inline assembly with Groovy… Evil or awesome?] 90 | * http://melix.github.io/blog/2013/01/31/using_groovy_to_play_with.html[Using Groovy to play with invokedynamic] 91 | 92 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2011-2015 Cédric Champeau 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | plugins { 20 | id 'com.gradle.build-scan' version '1.0' 21 | id 'com.jfrog.artifactory' version '4.4.0' 22 | id 'com.jfrog.bintray' version '1.7' 23 | } 24 | 25 | buildScan { 26 | licenseAgreementUrl = 'https://gradle.com/terms-of-service' 27 | licenseAgree = 'yes' 28 | } 29 | 30 | apply plugin: 'idea' 31 | 32 | description = "Provides an AST Transformation which allows to write the body of a method as bytecode" 33 | 34 | apply from:'gradle/compile.gradle' 35 | apply from:'gradle/credentials.gradle' 36 | apply from:'gradle/publishing.gradle' 37 | apply from:'gradle/artifactory.gradle' 38 | apply from:'gradle/bintray.gradle' 39 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | group=me.champeau.groovy 2 | version=0.2.2-SNAPSHOT 3 | 4 | -------------------------------------------------------------------------------- /gradle/artifactory.gradle: -------------------------------------------------------------------------------- 1 | // handles distribution of snapshots to Artifactory (oss.jfrog.org) 2 | 3 | artifactory { 4 | contextUrl = 'http://oss.jfrog.org/artifactory' 5 | publish { 6 | repository { 7 | repoKey = project.version.endsWith('SNAPSHOT')?'oss-snapshot-local':'oss-release-local' 8 | username = bintrayUsername 9 | password = bintrayKey 10 | } 11 | defaults { 12 | publications ('mavenJava') 13 | } 14 | } 15 | resolve { 16 | repository { 17 | repoKey = 'libs-release' 18 | } 19 | } 20 | 21 | } 22 | 23 | artifactoryPublish { 24 | onlyIf { 25 | def pullRequest = System.getenv('TRAVIS_PULL_REQUEST') 26 | 27 | !pullRequest || pullRequest=='false' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gradle/bintray.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // Handles publication of distributions to Bintray 15 | 16 | bintray { 17 | user = bintrayUsername 18 | key = bintrayKey 19 | publications = ['mavenJava'] 20 | pkg { 21 | repo = 'groovy-libs' 22 | name = 'bytecode-xform' 23 | desc = 'The @Bytecode AST transformation to write bytecode directly as a method body' 24 | licenses = ['Apache-2.0'] 25 | labels = ['bytecode','groovy'] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /gradle/compile.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | apply plugin: 'jacoco' 3 | 4 | repositories { 5 | jcenter() 6 | } 7 | 8 | // external dependencies 9 | dependencies { 10 | compile 'org.codehaus.groovy:groovy:2.4.7' 11 | testCompile("org.spockframework:spock-core:1.0-groovy-2.4") { 12 | exclude group: 'org.codehaus.groovy' 13 | } 14 | testCompile "junit:junit:4.12" 15 | } 16 | 17 | sourceCompatibility=1.6 18 | targetCompatibility=1.6 19 | 20 | compileGroovy.options.encoding = 'UTF-8' 21 | 22 | // custom tasks for creating source/javadoc jars 23 | task sourcesJar(type: Jar, dependsOn: classes) { 24 | classifier = 'sources' 25 | from sourceSets.main.allSource 26 | } 27 | 28 | task javadocJar(type: Jar, dependsOn: javadoc) { 29 | classifier = 'javadoc' 30 | from javadoc.destinationDir 31 | } 32 | -------------------------------------------------------------------------------- /gradle/credentials.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | ext.bintrayUsername = project.hasProperty('bintrayUsername')?project.getProperty('bintrayUsername'):System.getenv('BINTRAY_USER')?:'' 15 | ext.bintrayKey = project.hasProperty('bintrayKey')?project.getProperty('bintrayKey'):System.getenv('BINTRAY_KEY')?:'' 16 | -------------------------------------------------------------------------------- /gradle/publishing.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // configuration of the Maven artifacts 15 | apply plugin: 'maven-publish' 16 | 17 | artifacts { 18 | archives sourcesJar, javadocJar 19 | } 20 | 21 | publishing { 22 | publications { 23 | mavenJava(MavenPublication) { 24 | from components.java 25 | 26 | artifact sourcesJar { 27 | classifier "sources" 28 | } 29 | 30 | artifact javadocJar { 31 | classifier "javadoc" 32 | } 33 | 34 | pom.withXml { 35 | def Node root = asNode() 36 | 37 | root.appendNode('name', '@Bytecode AST transformation') 38 | root.appendNode('description', 'Allows writing bytecode directly as a method body in Groovy classes') 39 | root.appendNode('url', 'https://github.com/melix/groovy-bytecode-ast') 40 | 41 | def issues = root.appendNode( 'issueManagement' ) 42 | issues.appendNode( 'system', 'github' ) 43 | issues.appendNode( 'url', 'https://github.com/melix/groovy-bytecode-ast/issues' ) 44 | 45 | def scm = root.appendNode( 'scm' ) 46 | scm.appendNode( 'url', 'https://github.com/melix/groovy-bytecode-ast' ) 47 | scm.appendNode( 'connection', 'scm:git:https://github.com/melix/groovy-bytecode-ast.git' ) 48 | scm.appendNode( 'developerConnection', 'scm:git:git@github.com:melix/groovy-bytecode-ast.git' ) 49 | 50 | def license = root.appendNode( 'licenses' ).appendNode( 'license' ); 51 | license.appendNode( 'name', 'The Apache Software License, Version 2.0' ) 52 | license.appendNode( 'url', 'http://www.apache.org/licenses/LICENSE-2.0.txt' ) 53 | license.appendNode( 'distribution', 'repo' ) 54 | 55 | def dev = root.appendNode( 'developers' ).appendNode( 'developer' ); 56 | dev.appendNode( 'id', 'melix' ) 57 | dev.appendNode( 'name', 'Cédric Champeau' ) 58 | dev.appendNode( 'organization', 'Groovy' ) 59 | dev.appendNode( 'organizationUrl', 'http://groovy-lang.org' ) 60 | 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/groovy-bytecode-ast/dde14e3f2a99a798a92893d7a26cf483b978ae6b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jan 10 15:14:59 EST 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-3.0-milestone-2-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 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 %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="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 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='bytecode-xform' 2 | -------------------------------------------------------------------------------- /src/main/groovy/groovyx/ast/bytecode/Bytecode.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 Cédric Champeau 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 | * http://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 | package groovyx.ast.bytecode 22 | 23 | /** 24 | * Created by IntelliJ IDEA. 25 | * User: cedric 26 | * Date: 14/01/11 27 | * Time: 21:51 28 | */ 29 | 30 | import java.lang.annotation.ElementType 31 | import java.lang.annotation.Retention 32 | import java.lang.annotation.RetentionPolicy 33 | import java.lang.annotation.Target 34 | import org.codehaus.groovy.transform.GroovyASTTransformationClass 35 | 36 | @Retention(RetentionPolicy.SOURCE) 37 | @Target([ElementType.METHOD]) 38 | @GroovyASTTransformationClass(["groovyx.ast.bytecode.BytecodeASTTransformation"]) 39 | public @interface Bytecode { 40 | boolean lineNumbers() default false; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/groovy/groovyx/ast/bytecode/BytecodeASTTransformation.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 Cédric Champeau 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 | * http://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 | package groovyx.ast.bytecode 22 | 23 | import groovyjarjarasm.asm.Handle 24 | import groovyjarjarasm.asm.Type 25 | import groovyjarjarasm.asm.Label 26 | import groovyjarjarasm.asm.MethodVisitor 27 | import groovyjarjarasm.asm.Opcodes 28 | import org.codehaus.groovy.ast.ASTNode 29 | import org.codehaus.groovy.ast.AnnotationNode 30 | import org.codehaus.groovy.ast.MethodNode 31 | import org.codehaus.groovy.ast.Parameter 32 | import org.codehaus.groovy.ast.stmt.BlockStatement 33 | import org.codehaus.groovy.ast.stmt.ExpressionStatement 34 | import org.codehaus.groovy.ast.stmt.ReturnStatement 35 | import org.codehaus.groovy.classgen.BytecodeInstruction 36 | import org.codehaus.groovy.classgen.BytecodeSequence 37 | import org.codehaus.groovy.classgen.asm.BytecodeHelper 38 | import org.codehaus.groovy.control.CompilePhase 39 | import org.codehaus.groovy.control.SourceUnit 40 | import org.codehaus.groovy.transform.ASTTransformation 41 | import org.codehaus.groovy.transform.GroovyASTTransformation 42 | import org.codehaus.groovy.ast.expr.* 43 | import org.codehaus.groovy.ast.stmt.Statement 44 | import org.codehaus.groovy.ast.Variable 45 | import org.codehaus.groovy.ast.ClassNode 46 | import org.codehaus.groovy.ast.ClassHelper 47 | 48 | import java.lang.invoke.MethodType 49 | 50 | @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) 51 | class BytecodeASTTransformation implements ASTTransformation, Opcodes { 52 | private SourceUnit sourceUnit 53 | private boolean showLineNumbers = false 54 | 55 | void visit(ASTNode[] nodes, SourceUnit source) { 56 | def ann = nodes[0] 57 | def meth = nodes[1] 58 | if (ann instanceof AnnotationNode && meth instanceof MethodNode) { 59 | extractOptions(ann) 60 | def code = meth.code 61 | def statements = code instanceof BlockStatement?code.statements:[code] 62 | meth.code = new BytecodeSequence(new BytecodeGenerator(meth, statements)) 63 | sourceUnit = source 64 | } else { 65 | throw new IllegalArgumentException("The @Bytecode annotation is only supported on methods") 66 | } 67 | } 68 | 69 | private final extractOptions(AnnotationNode node) { 70 | def member = node.getMember('lineNumbers') 71 | if (member instanceof ConstantExpression) { 72 | showLineNumbers = member.value as boolean 73 | } 74 | } 75 | 76 | /** 77 | * Given a String of the form '.field' or 'fqn.Class.field', returns 78 | * a couple ('fqn.Class', 'field') where the class is always replaced with the 79 | * enclosing class if not specified. 80 | */ 81 | private static def extractClazzAndFieldOrMethod(classExpr, meth) { 82 | def clazz, field 83 | if (classExpr[0] == '.') { 84 | clazz = meth.declaringClass.name 85 | field = classExpr[1.. instructions 101 | private final List localVariables = [] 102 | 103 | BytecodeGenerator(MethodNode method, Collection instructions) { 104 | this.meth = method 105 | this.instructions = instructions 106 | } 107 | 108 | @Override 109 | void visit(MethodVisitor mv) { 110 | def labels = [:].withDefault { throw new IllegalArgumentException("Label [${it}] is not defined")} 111 | // perform first visit to collect labels 112 | collectLabels(labels) 113 | Label start = new Label() 114 | labels.start = start 115 | mv.visitLabel(start) 116 | // second iteration transforms each instruction into bytecode visitor instructions 117 | visitInstructions(mv, labels) 118 | Label end = new Label() 119 | labels.end = end 120 | mv.visitLabel(end) 121 | // declare local variables 122 | int idx = meth.isStatic()?0:1 123 | for (Parameter p : meth.parameters) { 124 | mv.visitLocalVariable(p.name, BytecodeHelper.getTypeDescription(p.originType), null, start, end, idx++) 125 | } 126 | localVariables.each { it.call() } 127 | } 128 | 129 | private def visitInstructions(MethodVisitor mv, Map labels) { 130 | instructions.each { stmt -> 131 | printLineNumber(stmt, mv) 132 | if (stmt.statementLabel) { 133 | mv.visitLabel(labels[stmt.statementLabel]) 134 | } 135 | if (stmt instanceof ReturnStatement) { 136 | mv.visitInsn(Opcodes.RETURN) 137 | } else if (stmt instanceof ExpressionStatement) { 138 | def expression = stmt.expression 139 | if (expression instanceof VariableExpression) { 140 | visitVariableExpression(expression, mv, labels) 141 | } else if (expression instanceof MethodCallExpression) { 142 | visitMethodCallExpression(expression, mv, labels, meth) 143 | } else { 144 | unsupportedBytecodeOperation(expression) 145 | } 146 | } 147 | } 148 | } 149 | 150 | private void printLineNumber(ASTNode stmt, MethodVisitor mv) { 151 | if (showLineNumbers && stmt.lineNumber > 0) { 152 | def label = new Label() 153 | mv.visitLabel(label) 154 | mv.visitLineNumber(stmt.lineNumber, label) 155 | } 156 | } 157 | 158 | private def visitMethodCallExpression(MethodCallExpression expression, MethodVisitor mv, Map labels, meth) { 159 | if (expression.objectExpression instanceof VariableExpression && expression.arguments instanceof ArgumentListExpression) { 160 | if (expression.objectExpression.text == "this") { 161 | def opcode = expression.methodAsString.toUpperCase() 162 | ArgumentListExpression args = expression.arguments 163 | switch (opcode) { 164 | case '_GOTO': 165 | mv.visitJumpInsn(GOTO, labels[args.expressions[0].text]) 166 | break; 167 | case '_NEW': 168 | case 'NEWOBJECT': 169 | mv.visitTypeInsn(NEW, internalClassName(args.expressions[0])) 170 | break; 171 | case '_INSTANCEOF': 172 | mv.visitTypeInsn(INSTANCEOF, internalClassName(args.expressions[0])) 173 | break; 174 | case 'IF_ICMPGE': 175 | case 'IF_ICMPLE': 176 | case 'IF_ICMPNE': 177 | case 'IF_ICMPLT': 178 | case 'IF_ICMPGT': 179 | case 'IF_ICMPEQ': 180 | case 'IF_ACMPEQ': 181 | case 'IF_ACMPNE': 182 | case 'IFEQ': 183 | case 'IFGE': 184 | case 'IFGT': 185 | case 'IFLE': 186 | case 'IFLT': 187 | case 'IFNE': 188 | case 'IFNONNULL': 189 | case 'IFNULL': 190 | mv.visitJumpInsn(Opcodes."${opcode}", labels[args.expressions[0].text]) 191 | break; 192 | case 'ALOAD': 193 | case 'ILOAD': 194 | case 'LLOAD': 195 | case 'FLOAD': 196 | case 'DLOAD': 197 | case 'ASTORE': 198 | case 'ISTORE': 199 | case 'FSTORE': 200 | case 'LSTORE': 201 | case 'DSTORE': 202 | mv.visitVarInsn(Opcodes."${opcode}", args.expressions[0].text as int) 203 | break; 204 | case 'IINC': 205 | mv.visitIincInsn(args.expressions[0].text as int, args.expressions[1].text as int) 206 | break; 207 | case 'INVOKEDYNAMIC': 208 | visitInvokeDynamic(mv, args) 209 | break; 210 | case 'INVOKEVIRTUAL': 211 | case 'INVOKESTATIC': 212 | case 'INVOKEINTERFACE': 213 | case 'INVOKESPECIAL': 214 | visitMethodInvoke(mv, opcode, meth, args) 215 | break; 216 | case 'FRAME': 217 | // frames only supported in JDK 1.6+ 218 | break; 219 | case 'CHECKCAST': 220 | mv.visitTypeInsn(CHECKCAST, internalClassName(args.expressions[0])) 221 | break; 222 | case 'LDC': 223 | mv.visitLdcInsn(args.expressions[0].value) 224 | break; 225 | case 'GETFIELD': 226 | case 'PUTFIELD': 227 | case 'GETSTATIC': 228 | case 'PUTSTATIC': 229 | visitFieldInstruction(mv, meth, opcode, args) 230 | break; 231 | case 'BIPUSH': 232 | case 'SIPUSH': 233 | mv.visitIntInsn(Opcodes."${opcode}", args.expressions[0].text as int) 234 | break; 235 | case 'NEWARRAY': 236 | if (args.expressions[0] instanceof ConstantExpression) { 237 | mv.visitIntInsn(Opcodes."${opcode}", Opcodes."${args.expressions[0].text.toUpperCase()}") 238 | } else if (args.expressions[0] instanceof ClassExpression) { 239 | mv.visitIntInsn(Opcodes."${opcode}", Opcodes."T_${args.expressions[0].type.nameWithoutPackage.toUpperCase()}") 240 | } else if (args.expressions[0] instanceof VariableExpression) { 241 | mv.visitIntInsn(Opcodes."${opcode}", Opcodes."${args.expressions[0].text.toUpperCase()}") 242 | } else { 243 | unsupportedBytecodeOperation(expression) 244 | } 245 | break; 246 | case 'ANEWARRAY': 247 | mv.visitTypeInsn(ANEWARRAY, internalClassName(args.expressions[0])); 248 | break; 249 | case 'MULTIANEWARRAY': 250 | if (args.expressions.size()==2) { 251 | // legacy syntax 252 | mv.visitMultiANewArrayInsn(internalClassName(args.expressions[0]), args.expressions[1].text as int) 253 | } else { 254 | unsupportedBytecodeOperation(expression) 255 | } 256 | break; 257 | case 'TRYCATCHBLOCK': 258 | if (args.expressions.size() != 4) throw new IllegalArgumentException("Bytecode operation unsupported [trycatchblock requires exactly 4 parameters] : " + expression); 259 | def tcargs = args.expressions 260 | mv.visitTryCatchBlock(labels[tcargs[0].text], labels[tcargs[1].text], labels[tcargs[2].text], internalClassName(tcargs[3])) 261 | break 262 | case 'LOCALVARIABLE': 263 | if (args.expressions.size() != 6) throw new IllegalArgumentException("Bytecode operation unsupported [localvariable requires exactly 4 parameters] : $expression.text") 264 | def ag = args.expressions 265 | localVariables << { 266 | mv.visitLocalVariable( 267 | ag[0].text, 268 | ag[1].text, 269 | ag[1].text, 270 | labels[ag[3].text], 271 | labels[ag[4].text], 272 | ag[5].value as int) 273 | } 274 | break 275 | default: 276 | unsupportedBytecodeOperation(expression) 277 | } 278 | } else { 279 | unsupportedBytecodeOperation(expression) 280 | } 281 | } else if (expression.objectExpression instanceof VariableExpression && expression.arguments instanceof TupleExpression) { 282 | if (expression.method.text == 'lookupswitch') { 283 | processLookupSwitch(expression, mv, labels) 284 | } else if (expression.method.text == 'tableswitch') { 285 | processTableSwitch(expression, mv, labels) 286 | } else if (expression.method.text == 'go' || expression.method.text=='instance') { 287 | if (expression.arguments instanceof TupleExpression) { 288 | TupleExpression args = expression.arguments 289 | if (args.expressions.size()==1) { 290 | def arg = args.expressions[0] 291 | if (arg instanceof NamedArgumentListExpression) { 292 | arg.mapEntryExpressions.each { MapEntryExpression mapEntryExpression -> 293 | if (mapEntryExpression.keyExpression.text == 'to' && expression.method.text=='go') { 294 | mv.visitJumpInsn(GOTO, labels[mapEntryExpression.valueExpression.text]) 295 | } else if (mapEntryExpression.keyExpression.text == 'of' && expression.method.text=='instance') { 296 | mv.visitTypeInsn(INSTANCEOF, internalClassName(mapEntryExpression.valueExpression)) 297 | } else { 298 | throw new IllegalArgumentException("Bytecode operation supported : $expression") 299 | } 300 | } 301 | } else { 302 | unsupportedBytecodeOperation(expression); 303 | } 304 | } else { 305 | unsupportedBytecodeOperation(expression); 306 | } 307 | } else { 308 | unsupportedBytecodeOperation(expression) 309 | } 310 | } else { 311 | unsupportedBytecodeOperation(expression) 312 | } 313 | } else { 314 | unsupportedBytecodeOperation(expression) 315 | } 316 | } 317 | 318 | private static unsupportedBytecodeOperation(expression) { 319 | throw new IllegalArgumentException("Bytecode operation unsupported : " + expression) 320 | } 321 | 322 | private static String internalClassName(Expression expr) { 323 | if (expr instanceof ConstantExpression) { 324 | expr.value==null?null:expr.text 325 | } else if (expr instanceof ClassExpression) { 326 | BytecodeHelper.getClassInternalName(expr.type) 327 | } else { 328 | unsupportedBytecodeOperation(expr) 329 | } 330 | } 331 | 332 | private def visitFieldInstruction(MethodVisitor mv, meth, opcode, ArgumentListExpression args) { 333 | def clazz, field, descriptor 334 | 335 | // syntax of the form: getstatic name >> String 336 | if (args.expressions[0] instanceof BinaryExpression && args.expressions[0].operation.text == '>>') { 337 | BinaryExpression binExpr = args.expressions[0] 338 | if (binExpr.rightExpression instanceof ClassExpression) { 339 | //descriptor = Type.getDescriptor(args.expressions[0].rightExpression.type.typeClass) 340 | descriptor = BytecodeHelper.getTypeDescription(args.expressions[0].rightExpression.type) 341 | } else { 342 | throw new IllegalArgumentException("Expected a class expression on the right of '>>'") 343 | } 344 | 345 | if (binExpr.leftExpression instanceof Variable) { 346 | clazz = meth.declaringClass.name 347 | field = binExpr.leftExpression.name 348 | } else if (binExpr.leftExpression instanceof PropertyExpression) { 349 | PropertyExpression propExp = binExpr.leftExpression 350 | if (propExp.objectExpression instanceof ClassExpression) { 351 | //clazz = Type.getInternalName(propExp.objectExpression.type.typeClass) 352 | clazz = BytecodeHelper.getClassInternalName(propExp.objectExpression.type) 353 | field = propExp.property.text 354 | } else { 355 | throw new IllegalArgumentException( 356 | "Expected a class expression but got a ${propExp.objectExpression.class.simpleName}") 357 | } 358 | } else { 359 | throw new IllegalArgumentException("Expected a variable or a property on the left of '>>") 360 | } 361 | 362 | } else { // usual syntax 363 | def classExpr = args.expressions[0].text 364 | (clazz, field) = extractClazzAndFieldOrMethod(classExpr, meth) 365 | descriptor = args.expressions[1].text 366 | } 367 | 368 | mv.visitFieldInsn(Opcodes."${opcode}", clazz, field, descriptor) 369 | } 370 | 371 | private def visitMethodInvoke(MethodVisitor mv, opcode, meth, ArgumentListExpression args) { 372 | def clazz, call, signature 373 | 374 | // syntax of the form: invokevirtual SomeClass.method(double[], String) >> int[] 375 | if (args.expressions[0] instanceof BinaryExpression && args.expressions[0].operation.text == '>>') { 376 | // return type is what's on the right of the >> binary expression 377 | ClassNode returnTypeClassNode = args.expressions[0].rightExpression.type 378 | def returnType = returnTypeClassNode == ClassHelper.VOID_TYPE ? "V" : BytecodeHelper.getTypeDescription(returnTypeClassNode) 379 | 380 | MethodCallExpression methCall = args.expressions[0].leftExpression 381 | 382 | // the callee is the subject on which the method is invoked 383 | def callee = methCall.objectExpression 384 | 385 | // either the type of the class is explicitely defined 386 | if (callee instanceof ClassExpression) { 387 | clazz = BytecodeHelper.getClassInternalName(callee.type) 388 | } 389 | // or the call is made on this 390 | else if (callee instanceof VariableExpression && callee.name == "this") { 391 | clazz = meth.declaringClass.name 392 | } 393 | // otherwise it's an error 394 | else { 395 | throw new IllegalArgumentException("Expected a class expression or variable expression") 396 | } 397 | 398 | signature = '(' + methCall.arguments.expressions.collect { ClassExpression ce -> 399 | Type.getDescriptor(ce.type.typeClass) 400 | }.join('') + ')' + returnType 401 | 402 | call = methCall.methodAsString 403 | } else { // usual syntax 404 | def classExpr = args.expressions[0].text 405 | (clazz, call) = extractClazzAndFieldOrMethod(classExpr, meth) 406 | signature = args.expressions[1].text 407 | } 408 | 409 | mv.visitMethodInsn(Opcodes."${opcode}", clazz, call, signature) 410 | } 411 | 412 | private String signature(ListExpression list) { 413 | MethodCallExpression mce = new MethodCallExpression( 414 | new StaticMethodCallExpression( 415 | ClassHelper.make(MethodType), 416 | "methodType", 417 | new ArgumentListExpression(list.expressions) 418 | ), 419 | "toMethodDescriptorString", 420 | ArgumentListExpression.EMPTY_ARGUMENTS 421 | ) 422 | ExpressionEvaluator.evaluate(mce) 423 | } 424 | 425 | private def visitInvokeDynamic(MethodVisitor mv, ArgumentListExpression args) { 426 | sourceUnit.configuration.targetBytecode = '1.7' 427 | def exprs = args.getExpressions() 428 | if (exprs.size()<3) { 429 | throw new IllegalArgumentException("Not enough arguments for an invokedynamic call") 430 | } 431 | // invokedynamic "name", "signature()I", [H_INVOKESTATIC, ...] 432 | String name = exprs[0].text 433 | String desc 434 | def handleCreateArgs = new ArgumentListExpression() 435 | def handleExpressions = exprs[2].expressions 436 | for (int i=0; i3?exprs.subList(3, exprs.size()).collect { ConstantExpression ce -> 445 | ce.value}:[]) as Object[] 446 | def argTypes = exprs[1] 447 | if (argTypes instanceof ConstantExpression) { 448 | desc = argTypes.text 449 | } else if (argTypes instanceof ListExpression) { 450 | desc = signature(argTypes) 451 | } else { 452 | throw new IllegalArgumentException("Second argument of invokedynamic call must be a list of classes") 453 | } 454 | 455 | Handle handle = ExpressionEvaluator.evaluate(handleExpr) 456 | mv.visitInvokeDynamicInsn(name, desc, handle, *extraArgs) 457 | } 458 | 459 | private def visitVariableExpression(VariableExpression expression, MethodVisitor mv, Map labels) { 460 | def text = expression.text.toLowerCase() 461 | if (text ==~ /l[0-9]+/) { 462 | mv.visitLabel(labels[text]) 463 | } else if (text == 'vreturn') { 464 | // vreturn replaces the regular "return" bytecode statement 465 | mv.visitInsn(RETURN) 466 | } else if (Instructions.UNIT_OPS.contains(text)) { 467 | mv.visitInsn(Opcodes."${text.toUpperCase()}") 468 | } else if (text =~ /(load|store)_[0-4]/) { 469 | def (var, cpt) = text.split("_") 470 | mv.visitVarInsn(Opcodes."${var.toUpperCase()}", cpt as int) 471 | } else { 472 | throw new IllegalArgumentException("Bytecode operation unsupported : " + text); 473 | } 474 | } 475 | 476 | private def collectLabels(labels) { 477 | instructions.each { Statement stmt -> 478 | if (stmt.statementLabel) { 479 | labels.put(stmt.statementLabel, new Label()) 480 | } 481 | if (stmt instanceof ExpressionStatement) { 482 | def expression = stmt.expression 483 | if (expression instanceof VariableExpression) { 484 | def text = expression.text 485 | if (text ==~ /l[0-9]+/) { 486 | labels.put(text, new Label()) 487 | } 488 | } 489 | } 490 | } 491 | } 492 | 493 | private def processLookupSwitch(MethodCallExpression expression, MethodVisitor mv, labels) { 494 | if (expression.arguments.expressions && expression.arguments.expressions[0] instanceof NamedArgumentListExpression) { 495 | def defaultLabel = null 496 | def values = [] 497 | def targetLabels = [] 498 | def exprs = expression.arguments.expressions[0].mapEntryExpressions 499 | exprs.each { MapEntryExpression mapEntryExpression -> 500 | def key = mapEntryExpression.keyExpression.value 501 | switch (key) { 502 | case 'default': 503 | defaultLabel = labels[mapEntryExpression.valueExpression.text] 504 | break; 505 | default: 506 | values << (key as int) 507 | targetLabels << labels[mapEntryExpression.valueExpression.text] 508 | } 509 | } 510 | if (defaultLabel == null) throw new IllegalArgumentException("Bytecode operation unsupported [lookupswitch must provide default label]: " + expression); 511 | mv.visitLookupSwitchInsn(defaultLabel, values as int[], targetLabels as Label[]) 512 | } else { 513 | unsupportedBytecodeOperation(expression) 514 | } 515 | } 516 | 517 | private def processTableSwitch(MethodCallExpression expression, MethodVisitor mv, labels) { 518 | if (expression.arguments.expressions && expression.arguments.expressions[0] instanceof NamedArgumentListExpression) { 519 | def defaultLabel = null 520 | def values = [] 521 | def targetLabels = [] 522 | def exprs = expression.arguments.expressions[0].mapEntryExpressions 523 | exprs.each { MapEntryExpression mapEntryExpression -> 524 | def key = mapEntryExpression.keyExpression.value 525 | switch (key) { 526 | case 'default': 527 | defaultLabel = labels[mapEntryExpression.valueExpression.text] 528 | break; 529 | default: 530 | values << (key as int) 531 | targetLabels << labels[mapEntryExpression.valueExpression.text] 532 | } 533 | } 534 | if (defaultLabel == null) throw new IllegalArgumentException("Bytecode operation unsupported [tableswitch must provide default label]: " + expression); 535 | values[1.. 536 | if (it != values[i] + 1) throw new IllegalArgumentException("Bytecode operation unsupported [tableswitch must consist of sequential values]: " + expression) 537 | } 538 | mv.visitTableSwitchInsn(values.min(), values.max(), defaultLabel, targetLabels as Label[]) 539 | } else { 540 | unsupportedBytecodeOperation(expression) 541 | } 542 | } 543 | 544 | } 545 | } 546 | -------------------------------------------------------------------------------- /src/main/groovy/groovyx/ast/bytecode/ExpressionEvaluator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2003-2012 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package groovyx.ast.bytecode; 17 | 18 | import groovy.lang.GroovyObject; 19 | import org.codehaus.groovy.GroovyBugError; 20 | import org.codehaus.groovy.ast.ClassNode; 21 | import org.codehaus.groovy.ast.MethodNode; 22 | import org.codehaus.groovy.ast.expr.Expression; 23 | import org.codehaus.groovy.ast.stmt.ExpressionStatement; 24 | import org.codehaus.groovy.ast.stmt.Statement; 25 | import org.codehaus.groovy.control.CompilationUnit; 26 | import org.codehaus.groovy.control.Phases; 27 | import org.codehaus.groovy.control.SourceUnit; 28 | import org.codehaus.groovy.tools.GroovyClass; 29 | 30 | public class ExpressionEvaluator { 31 | public static Object evaluate(Expression expr) { 32 | CompilationUnit cu = new CompilationUnit(); 33 | SourceUnit dummy = SourceUnit.create("dummy", ""); 34 | cu.addSource(dummy); 35 | cu.compile(Phases.CONVERSION); 36 | ClassNode classNode = dummy.getAST().getClasses().get(0); 37 | MethodNode run = classNode.getMethods("run").get(0); 38 | Statement code = new ExpressionStatement(expr); 39 | run.setCode(code); 40 | cu.compile(Phases.CLASS_GENERATION); 41 | GroovyClass clazz = (GroovyClass) cu.getClasses().get(0); 42 | Class aClass = cu.getClassLoader().defineClass(clazz.getName(), clazz.getBytes()); 43 | try { 44 | return ((GroovyObject) aClass.newInstance()).invokeMethod("run", null); 45 | } catch (InstantiationException e) { 46 | throw new GroovyBugError(e); 47 | } catch (IllegalAccessException e) { 48 | throw new GroovyBugError(e); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/groovy/groovyx/ast/bytecode/Instructions.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 C�dric Champeau 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 | * http://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 | 22 | 23 | package groovyx.ast.bytecode 24 | 25 | /** 26 | * Created by IntelliJ IDEA. 27 | * User: cedric 28 | * Date: 15/01/11 29 | * Time: 23:45 30 | */ 31 | 32 | /** 33 | * List of instructions supported by the AST transform 34 | */ 35 | class Instructions { 36 | 37 | /** 38 | * Unit operations except load_X / store_X 39 | */ 40 | public static final Set UNIT_OPS = [ 41 | "aaload", 42 | "aastore", 43 | "aconst_null", 44 | "areturn", 45 | "athrow", 46 | "arraylength", 47 | "baload", "bastore", 48 | "caload", "castore", 49 | "d2f", "d2i", "d2l", "dadd", 50 | "daload", "dastore", 51 | "dcmpg", "dcmpl", 52 | "dconst_0", "dconst_1", "ddiv", "dload", 53 | "dmul", "dneg", "drem", "dreturn", 54 | "dsub", "dup", "dup2", "dup2_x1", "dup2_x2", "dup_x1", "dup_x2", 55 | "f2d", "f2i", "f2l", "fadd", "faload", "fastore", 56 | "fcmpg", "fcmpl", 57 | "fconst_0", "fconst_1", "fconst_2", 58 | "fdiv", "fload", 59 | "fmul", "fneg", "frem", "freturn", "fstore", 60 | "fsub", "i2b", "i2c", "i2d", "i2f", "i2l", "i2s", 61 | "iadd", "iaload", "iand", "iastore", 62 | "iconst_0", "iconst_1", "iconst_2", "iconst_3", "iconst_4", "iconst_5", "iconst_m1", 63 | "idiv", 64 | "imul", "ineg", "ior", "irem", "ireturn", 65 | "ishl", "ishr", 66 | "isub", "iushr", "ixor", 67 | "l2d", "l2f", "l2i", "ladd", 68 | "laload", 69 | "land", "lastore", "lcmp", 70 | "lconst_0", "lconst_1", 71 | "ldiv", 72 | "lmul", "lneg", "lor", "lrem", 73 | "lreturn", 74 | "lshl", "lshr", 75 | "lsub", "lushr", "lxor", 76 | "monitorenter", "monitorexit", 77 | "nop", "pop", "pop2", 78 | "vreturn", // should be "return" 79 | "return", // added in 0.1.1 80 | "saload", "sastore", 81 | "swap" 82 | ] 83 | 84 | /** 85 | * Contains the list of JVM instructions which accept a single argument 86 | */ 87 | public static Set UNARY_OPS = [ 88 | "aload", 89 | "anewarray", 90 | "astore", 91 | "bipush", 92 | "checkcast", 93 | "dload", 94 | "dstore", 95 | "fload", 96 | "fstore", 97 | "_goto", // should be "goto", 98 | "if_acmpeq", 99 | "if_acmpne", 100 | "if_icmpeq", 101 | "if_icmpge", 102 | "if_icmpgt", 103 | "if_icmple", 104 | "if_icmplt", 105 | "if_icmpne", 106 | "ifeq", 107 | "ifge", 108 | "ifgt", 109 | "ifle", 110 | "iflt", 111 | "ifne", 112 | "ifnonnull", 113 | "ifnull", 114 | "iload", 115 | "_instanceof", // should be "instanceof" 116 | "istore", 117 | // "jsr", // deprecated 118 | "ldc", 119 | "lload", 120 | "lstore", 121 | "_new", // should be "new" 122 | "newarray", 123 | // "ret", // deprecated 124 | "sipush", 125 | ] 126 | } 127 | -------------------------------------------------------------------------------- /src/main/resources/bytecodeTransform.gdsl: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jason on 5/15/14. 3 | */ 4 | 5 | def mnemonics = [ 6 | 'aaload', 7 | 'aastore', 8 | 'aconst_null', 9 | 'aload', 10 | 'anewarray', 11 | 'arraylength', 12 | 'astore', 13 | 'athrow', 14 | 'baload', 15 | 'bastore', 16 | 'bipush', 17 | 'caload', 18 | 'castore', 19 | 'checkcast', 20 | 'd2f', 21 | 'd2i', 22 | 'd2l', 23 | 'dadd', 24 | 'daload', 25 | 'dastore', 26 | 'ddiv', 27 | 'dload', 28 | 'dmul', 29 | 'dneg', 30 | 'drem', 31 | 'dstore', 32 | 'dsub', 33 | 'dup', 34 | 'dup_x1', 35 | 'dup_x2', 36 | 'dup2', 37 | 'dup2_x1', 38 | 'dup2_x2', 39 | 'f2d', 40 | 'f2i', 41 | 'f2l', 42 | 'fadd', 43 | 'faload', 44 | 'fastore', 45 | 'fdiv', 46 | 'fload', 47 | 'fmul', 48 | 'fneg', 49 | 'frem', 50 | 'fstore', 51 | 'fsub', 52 | 'getfield', 53 | 'getstatic', 54 | 'goto', 55 | 'goto_w', 56 | 'i2b', 57 | 'i2c', 58 | 'i2d', 59 | 'i2f', 60 | 'i2l', 61 | 'i2s', 62 | 'iadd', 63 | 'iaload', 64 | 'iand', 65 | 'iastore', 66 | 'idiv', 67 | 'ifnonnull', 68 | 'ifnull', 69 | 'iinc', 70 | 'iload', 71 | 'imul', 72 | 'ineg', 73 | 'instanceof', 74 | 'invokedynamic', 75 | 'invokeinterface', 76 | 'invokespecial', 77 | 'invokestatic', 78 | 'invokevirtual', 79 | 'ior', 80 | 'irem', 81 | 'ishl', 82 | 'ishr', 83 | 'istore', 84 | 'isub', 85 | 'iushr', 86 | 'ixor', 87 | 'jsr', 88 | 'jsr_w', 89 | 'l2d', 90 | 'l2f', 91 | 'l2i', 92 | 'ladd', 93 | 'laload', 94 | 'land', 95 | 'lastore', 96 | 'lcmp', 97 | 'ldc', 98 | 'ldc_w', 99 | 'ldc2_w', 100 | 'ldiv', 101 | 'lload', 102 | 'lmul', 103 | 'lneg', 104 | 'lookupswitch', 105 | 'lor', 106 | 'lrem', 107 | 'lshl', 108 | 'lshr', 109 | 'lstore', 110 | 'lsub', 111 | 'lushr', 112 | 'lxor', 113 | 'monitorenter', 114 | 'monitorexit', 115 | 'multianewarray', 116 | 'new', 117 | 'newarray', 118 | 'nop', 119 | 'pop', 120 | 'pop2', 121 | 'putfield', 122 | 'putstatic', 123 | 'ret', 124 | 'saload', 125 | 'sastore', 126 | 'sipush', 127 | 'swap', 128 | 'tableswitch', 129 | 'wide', 130 | 'aload_0', 131 | 'aload_1', 132 | 'aload_2', 133 | 'aload_3', 134 | 'astore_0', 135 | 'astore_1', 136 | 'astore_2', 137 | 'astore_3', 138 | 'dcmpg', 139 | 'dcmpl', 140 | 'dstore_0', 141 | 'dstore_1', 142 | 'dstore_2', 143 | 'dstore_3', 144 | 'fcmpg', 145 | 'fcmpl', 146 | 'fconst_0', 147 | 'fconst_1', 148 | 'fconst_2', 149 | 'fload_0', 150 | 'fload_1', 151 | 'fload_2', 152 | 'fload_3', 153 | 'fstore_0', 154 | 'fstore_1', 155 | 'fstore_2', 156 | 'fstore_3', 157 | 'iconst_m1', 158 | 'iconst_0', 159 | 'iconst_1', 160 | 'iconst_2', 161 | 'iconst_3', 162 | 'iconst_4', 163 | 'iconst_5', 164 | 'if_acmpeq', 165 | 'if_acmpne', 166 | 'if_icmpeq', 167 | 'if_icmpne', 168 | 'if_icmplt', 169 | 'if_icmpge', 170 | 'if_icmpgt', 171 | 'if_icmple', 172 | 'ifeq', 173 | 'ifne', 174 | 'iflt', 175 | 'ifge', 176 | 'ifgt', 177 | 'ifle', 178 | 'iload_0', 179 | 'iload_1', 180 | 'iload_2', 181 | 'iload_3', 182 | 'istore_0', 183 | 'istore_1', 184 | 'istore_2', 185 | 'istore_3', 186 | 'lconst_0', 187 | 'lconst_1', 188 | 'lload_0', 189 | 'lload_1', 190 | 'lload_2', 191 | 'lload_3', 192 | 'lstore_0', 193 | 'lstore_1', 194 | 'lstore_2', 195 | 'lstore_3' 196 | ] 197 | mnemonics<<'_new' 198 | mnemonics<<'go to'<<'_goto' 199 | 200 | def returnOps = [areturn: Object.name, 201 | dreturn: Double.name, 202 | freturn: Float.name, 203 | ireturn: Integer.name, 204 | lreturn: Long.name, 205 | vreturn: Void.name] 206 | 207 | 208 | 209 | contributor(context(scope: annotatedScope(ctype: 'groovyx.ast.bytecode.Bytecode'))) { 210 | 211 | 212 | returnOps.each { op, cls -> variable name: op, type: cls } 213 | 214 | for (op in mnemonics) variable name: op, type: Closure.name 215 | } 216 | 217 | contributor(context(scope: annotatedScope(ctype: 'groovyx.ast.bytecode.Bytecode'),ctype: Object.name)){ 218 | method name: 'rightShift' , params: [arg:Object.name] 219 | } -------------------------------------------------------------------------------- /src/test/groovy/Helper.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 Cédric Champeau 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 | * http://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 | def shell = new GroovyShell() 21 | def run = shell.evaluate(""" 22 | @groovyx.ast.bytecode.Bytecode 23 | int[][] run(int x, int y) { 24 | iload_1 25 | iload_2 26 | return 27 | multianewarray '[[I',2 28 | return 29 | areturn 30 | } 31 | this.&run 32 | """) 33 | 34 | println """ 35 | L0 36 | LINENUMBER 27 L0 37 | ICONST_0 38 | ISTORE 2 39 | L1 40 | LINENUMBER 28 L1 41 | ICONST_0 42 | ISTORE 3 43 | L2 44 | FRAME APPEND [I I] 45 | ILOAD 3 46 | ILOAD 1 47 | IF_ICMPGE L3 48 | L4 49 | LINENUMBER 29 L4 50 | ILOAD 2 51 | ILOAD 1 52 | IADD 53 | ISTORE 2 54 | L5 55 | LINENUMBER 28 L5 56 | IINC 3 1 57 | GOTO L2 58 | L3 59 | LINENUMBER 31 L3 60 | FRAME CHOP 1 61 | ILOAD 2 62 | IRETURN""".toLowerCase() -------------------------------------------------------------------------------- /src/test/groovy/groovyx/ast/bytecode/BytecodeSpock.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 Cédric Champeau 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 | * http://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 | package groovyx.ast.bytecode 22 | 23 | import spock.lang.Specification 24 | import org.codehaus.groovy.control.MultipleCompilationErrorsException 25 | 26 | /** 27 | * Created by IntelliJ IDEA. 28 | * User: cedric 29 | * Date: 14/01/11 30 | * Time: 22:10 31 | */ 32 | 33 | class BytecodeSpock extends Specification { 34 | 35 | def "generate a Fibonacci method"() { 36 | def shell = new GroovyShell() 37 | def fib = shell.evaluate(""" 38 | @groovyx.ast.bytecode.Bytecode 39 | int fib(int n) { 40 | l0: 41 | iload 1 42 | iconst_2 43 | if_icmpge l1 44 | iload 1 45 | _goto l2 46 | l1: 47 | aload 0 48 | iload 1 49 | iconst_2 50 | isub 51 | invokevirtual '.fib','(I)I' 52 | aload 0 53 | iload 1 54 | iconst_1 55 | isub 56 | invokevirtual '.fib', '(I)I' 57 | iadd 58 | l2: 59 | ireturn 60 | } 61 | this.&fib 62 | """) 63 | 64 | expect: 65 | fib(i) == reference 66 | 67 | where: 68 | i | reference 69 | 0 | 0 70 | 1 | 1 71 | 2 | 1 72 | 3 | 2 73 | 4 | 3 74 | 5 | 5 75 | 6 | 8 76 | 7 | 13 77 | 8 | 21 78 | 9 | 34 79 | 10 | 55 80 | } 81 | 82 | def "test for loop with int index"() { 83 | def shell = new GroovyShell() 84 | def sum = shell.evaluate(""" 85 | /** 86 | * int sum = 0; 87 | * for (int i = 0; i < limit; i++) { 88 | * sum += i; 89 | * } 90 | * return sum; 91 | */ 92 | @groovyx.ast.bytecode.Bytecode 93 | int sum(int limit) { 94 | l0: 95 | iconst_0 96 | istore 2 97 | l1: 98 | iconst_0 99 | istore 3 100 | l2: 101 | iload 3 102 | iload 1 103 | if_icmpge l3 104 | l4: 105 | iload 2 106 | iload 3 107 | iadd 108 | istore 2 109 | l5: 110 | iinc 3,1 111 | _goto l2 112 | l3: 113 | iload 2 114 | ireturn 115 | } 116 | this.&sum 117 | """) 118 | 119 | expect: 120 | sum(i) == reference 121 | where: 122 | i | reference 123 | 0 | 0 124 | 1 | 0 125 | 2 | 1 126 | 3 | 3 127 | 4 | 6 128 | } 129 | 130 | def "tests toString"() { 131 | def shell = new GroovyShell() 132 | def toString = shell.evaluate(""" 133 | /** 134 | * return o.toString() 135 | */ 136 | @groovyx.ast.bytecode.Bytecode 137 | String toStr(Object o) { 138 | l0: 139 | aload 1 140 | invokevirtual 'java.lang.Object.toString','()Ljava/lang/String;' 141 | areturn 142 | } 143 | this.&toStr 144 | """) 145 | 146 | expect: 147 | toString(o) == o.toString() 148 | 149 | where: 150 | o << ["toto", 1, new GroovyShell()] 151 | } 152 | 153 | def "tests alternative syntax for method calls on this"() { 154 | def shell = new GroovyShell() 155 | def methCall = shell.evaluate(""" 156 | import groovyx.ast.bytecode.Bytecode 157 | 158 | int[] method(double[] d, String s) { [1, 2, 3, 4] as int[] } 159 | 160 | @Bytecode 161 | int[] callMethod() { 162 | aload 0 163 | iconst_0 164 | newarray t_double 165 | ldc "" 166 | // equivalent of: invokevirtual ".method", "([DLjava/lang/String;)[I" 167 | invokevirtual method(double[], String) >> int[] 168 | areturn 169 | } 170 | 171 | this.&callMethod 172 | """) 173 | 174 | expect: 175 | methCall().toList() == [1, 2, 3, 4] 176 | } 177 | 178 | def "tests alternative syntax for method calls on a class"() { 179 | def shell = new GroovyShell() 180 | def methCall = shell.evaluate(""" 181 | import groovyx.ast.bytecode.Bytecode 182 | 183 | class DummyClassWithWeirdMethod { 184 | static int[] method(double[] d, String s) { [1, 2, 3] as int[] } 185 | } 186 | 187 | @Bytecode 188 | int[] callMethod() { 189 | aload 0 190 | iconst_0 191 | newarray t_double 192 | ldc "" 193 | invokestatic DummyClassWithWeirdMethod.method(double[], String) >> int[] 194 | areturn 195 | } 196 | 197 | this.&callMethod 198 | """) 199 | 200 | expect: 201 | methCall().toList() == [1, 2, 3] 202 | } 203 | 204 | def "test static method call"() { 205 | def shell = new GroovyShell() 206 | def toString = shell.evaluate(""" 207 | 208 | static String echo(String text) { 209 | text 210 | } 211 | 212 | @groovyx.ast.bytecode.Bytecode 213 | String echo2(String o) { 214 | l0: 215 | aload 1 216 | invokestatic '.echo','(Ljava/lang/String;)Ljava/lang/String;' 217 | areturn 218 | } 219 | this.&echo2 220 | """) 221 | 222 | expect: 223 | toString(o) == o 224 | 225 | where: 226 | o << ["toto", "tata"] 227 | } 228 | 229 | def "test private method call"() { 230 | def shell = new GroovyShell() 231 | def toString = shell.evaluate(""" 232 | 233 | private String echo(String text) { 234 | text 235 | } 236 | 237 | @groovyx.ast.bytecode.Bytecode 238 | String echo2(String o) { 239 | l0: 240 | aload 0 241 | aload 1 242 | invokespecial '.echo','(Ljava/lang/String;)Ljava/lang/String;' 243 | areturn 244 | } 245 | this.&echo2 246 | """) 247 | 248 | expect: 249 | toString(o) == o 250 | 251 | where: 252 | o << ["toto", "tata"] 253 | } 254 | 255 | def "test jump to illegal label"() { 256 | def shell = new GroovyShell() 257 | 258 | when: 259 | shell.evaluate(""" 260 | 261 | @groovyx.ast.bytecode.Bytecode 262 | void jump() { 263 | _goto l1 264 | } 265 | """) 266 | 267 | then: 268 | def e = thrown(MultipleCompilationErrorsException) 269 | e.errorCollector.errors.size() == 1 270 | e.errorCollector.errors[0].cause.class == IllegalArgumentException 271 | 272 | } 273 | 274 | def "test void return"() { 275 | def shell = new GroovyShell() 276 | 277 | when: 278 | shell.evaluate(""" 279 | @groovyx.ast.bytecode.Bytecode 280 | void Void() { 281 | vreturn 282 | } 283 | """) 284 | 285 | then: 286 | notThrown(Exception) 287 | } 288 | 289 | def "should instantiate and return integer with autoboxing"() { 290 | def shell = new GroovyShell() 291 | def run = shell.evaluate(""" 292 | /** 293 | * return new Integer(i) 294 | */ 295 | @groovyx.ast.bytecode.Bytecode 296 | int run(int i) { 297 | _new 'java/lang/Integer' 298 | dup 299 | iload 1 300 | invokespecial 'java/lang/Integer.','(I)V' 301 | invokevirtual 'java/lang/Integer.intValue','()I' 302 | ireturn 303 | } 304 | this.&run 305 | """) 306 | 307 | expect: 308 | run(i) == i 309 | 310 | where: 311 | i << (0..10) 312 | } 313 | 314 | def "should instantiate and return integer with autoboxing and new syntax"() { 315 | def shell = new GroovyShell() 316 | def run = shell.evaluate(""" 317 | /** 318 | * return new Integer(i) 319 | */ 320 | @groovyx.ast.bytecode.Bytecode 321 | int run(int i) { 322 | newobject Integer 323 | dup 324 | iload 1 325 | invokespecial Integer.''(int) >> void 326 | invokevirtual Integer.intValue() >> int 327 | ireturn 328 | } 329 | this.&run 330 | """) 331 | 332 | expect: 333 | run(i) == i 334 | 335 | where: 336 | i << (0..10) 337 | } 338 | 339 | def "should pop value on stack"() { 340 | def shell = new GroovyShell() 341 | def run = shell.evaluate(""" 342 | /** 343 | * 1 344 | * return i 345 | */ 346 | @groovyx.ast.bytecode.Bytecode 347 | int run(int i) { 348 | iconst_1 349 | pop 350 | iload 1 351 | ireturn 352 | } 353 | this.&run 354 | """) 355 | 356 | expect: 357 | run(i) == i 358 | 359 | where: 360 | i << (0..10) 361 | } 362 | 363 | def "should pop double value on stack"() { 364 | def shell = new GroovyShell() 365 | def run = shell.evaluate(""" 366 | @groovyx.ast.bytecode.Bytecode 367 | int run(int i) { 368 | ldc 2.0d 369 | pop2 370 | iload 1 371 | ireturn 372 | } 373 | this.&run 374 | """) 375 | 376 | expect: 377 | run(i) == i 378 | 379 | where: 380 | i << (0..10) 381 | } 382 | 383 | def "should not accept (go of:) or (instance to:)"() { 384 | def shell = new GroovyShell() 385 | 386 | when: 387 | shell.evaluate(""" 388 | @groovyx.ast.bytecode.Bytecode 389 | void test() { 390 | $instr 391 | return 392 | } 393 | """) 394 | 395 | then: 396 | thrown(MultipleCompilationErrorsException) 397 | 398 | where: 399 | instr << ["go of: label", "instance to: Class"] 400 | } 401 | 402 | def "test lookupswitch statement"() { 403 | def shell = new GroovyShell() 404 | def run = shell.evaluate(""" 405 | @groovyx.ast.bytecode.Bytecode 406 | int run(int i) { 407 | iload_1 408 | lookupswitch( 409 | 2: l2, 410 | 5: l1, 411 | default: l3) 412 | l1: 413 | iconst_1 414 | ireturn 415 | l2: 416 | iconst_2 417 | ireturn 418 | l3: 419 | iconst_3 420 | ireturn 421 | } 422 | this.&run 423 | """) 424 | 425 | expect: 426 | run(x) == y 427 | 428 | where: 429 | x | y 430 | 0 | 3 431 | 1 | 3 432 | 2 | 2 433 | 3 | 3 434 | 4 | 3 435 | 5 | 1 436 | 6 | 3 437 | } 438 | 439 | def "test lookupswitch statement with custom labels"() { 440 | def shell = new GroovyShell() 441 | def run = shell.evaluate(""" 442 | @groovyx.ast.bytecode.Bytecode 443 | int run(int i) { 444 | iload_1 445 | lookupswitch( 446 | 2: section2, 447 | 5: section1, 448 | default: section3) 449 | section1: 450 | iconst_1 451 | ireturn 452 | section2: 453 | iconst_2 454 | ireturn 455 | section3: 456 | iconst_3 457 | ireturn 458 | } 459 | this.&run 460 | """) 461 | 462 | expect: 463 | run(x) == y 464 | 465 | where: 466 | x | y 467 | 0 | 3 468 | 1 | 3 469 | 2 | 2 470 | 3 | 3 471 | 4 | 3 472 | 5 | 1 473 | 6 | 3 474 | } 475 | 476 | def "test tableswitch statement"() { 477 | def shell = new GroovyShell() 478 | def run = shell.evaluate(""" 479 | @groovyx.ast.bytecode.Bytecode 480 | int run(int i) { 481 | iload_1 482 | tableswitch( 483 | 0: l2, 484 | 1: l1, 485 | default: l3) 486 | l1: 487 | iconst_1 488 | ireturn 489 | l2: 490 | iconst_2 491 | ireturn 492 | l3: 493 | iconst_3 494 | ireturn 495 | } 496 | this.&run 497 | """) 498 | 499 | expect: 500 | run(x) == y 501 | 502 | where: 503 | x | y 504 | 0 | 2 505 | 1 | 1 506 | 2 | 3 507 | 3 | 3 508 | 4 | 3 509 | 5 | 3 510 | 6 | 3 511 | } 512 | 513 | def "test multidimensional array instantiation"() { 514 | def shell = new GroovyShell() 515 | def run = shell.evaluate(""" 516 | @groovyx.ast.bytecode.Bytecode 517 | int[][] run(int x, int y) { 518 | iload_1 519 | iload_2 520 | multianewarray '[[I',2 521 | areturn 522 | } 523 | this.&run 524 | """) 525 | 526 | expect: 527 | run(x,y).length == x 528 | sizeOf2dLevel(run(x,y)) == y 529 | where: 530 | x | y 531 | 0 | 0 532 | 1 | 0 533 | 1 | 1 534 | 2 | 0 535 | 2 | 1 536 | 2 | 2 537 | } 538 | 539 | def "should handle null pointer exception safely"() { 540 | def shell = new GroovyShell() 541 | def safeToString = shell.evaluate(""" 542 | @groovyx.ast.bytecode.Bytecode 543 | public String safeToStringWithTryCatch(String str) { 544 | trycatchblock l0,l1,l2,'java/lang/NullPointerException' 545 | l0 546 | aload 1 547 | invokevirtual 'java/lang/String.toString', '()Ljava/lang/String;' 548 | l1 549 | areturn 550 | l2 551 | astore 2 552 | l3 553 | aconst_null 554 | areturn 555 | } 556 | this.&safeToStringWithTryCatch 557 | """) 558 | 559 | expect: 560 | safeToString(str) == str 561 | 562 | where: 563 | str << ['test',null] 564 | } 565 | 566 | def "should handle null pointer exception safely with new syntax"() { 567 | def shell = new GroovyShell() 568 | def safeToString = shell.evaluate(""" 569 | @groovyx.ast.bytecode.Bytecode 570 | public String safeToStringWithTryCatch(String str) { 571 | trycatchblock l0,l1,l2,NullPointerException 572 | l0 573 | aload 1 574 | invokevirtual String.toString() >> String 575 | l1 576 | areturn 577 | l2 578 | astore 2 579 | l3 580 | aconst_null 581 | areturn 582 | } 583 | this.&safeToStringWithTryCatch 584 | """) 585 | 586 | expect: 587 | safeToString(str) == str 588 | 589 | where: 590 | str << ['test',null] 591 | } 592 | 593 | def "should handle null pointer exception safely with not null return"() { 594 | def shell = new GroovyShell() 595 | def safeToString = shell.evaluate(""" 596 | @groovyx.ast.bytecode.Bytecode 597 | public String safeToStringWithTryCatch(String str) { 598 | trycatchblock l0,l1,l2,'java/lang/NullPointerException' 599 | trycatchblock l0,l1,l3,null 600 | trycatchblock l2,l4,l3,null 601 | trycatchblock l3,l5,l3,null 602 | l6: 603 | aconst_null 604 | astore_2 605 | l0: 606 | aload_1 607 | invokevirtual 'java/lang/String.toString', '()Ljava/lang/String;' 608 | astore_2 609 | l1: 610 | aload_2 611 | areturn 612 | l2: 613 | astore 3 614 | l7: 615 | ldc "null" 616 | astore 2 617 | l4: 618 | aload 2 619 | areturn 620 | l3: 621 | astore 4 622 | l5: 623 | aload 2 624 | areturn 625 | } 626 | this.&safeToStringWithTryCatch 627 | """) 628 | 629 | expect: 630 | safeToString(str) == tostr 631 | 632 | where: 633 | str | tostr 634 | 'test' | 'test' 635 | null | 'null' 636 | } 637 | 638 | def "should handle null pointer exception safely with not null return and new syntax"() { 639 | def shell = new GroovyShell() 640 | def safeToString = shell.evaluate(""" 641 | @groovyx.ast.bytecode.Bytecode 642 | public String safeToStringWithTryCatch(String str) { 643 | trycatchblock l0,l1,l2,NullPointerException 644 | trycatchblock l0,l1,l3,null 645 | trycatchblock l2,l4,l3,null 646 | trycatchblock l3,l5,l3,null 647 | l6: 648 | aconst_null 649 | astore_2 650 | l0: 651 | aload_1 652 | invokevirtual String.toString() >> String 653 | astore_2 654 | l1: 655 | aload_2 656 | areturn 657 | l2: 658 | astore 3 659 | l7: 660 | ldc "null" 661 | astore 2 662 | l4: 663 | aload 2 664 | areturn 665 | l3: 666 | astore 4 667 | l5: 668 | aload 2 669 | areturn 670 | } 671 | this.&safeToStringWithTryCatch 672 | """) 673 | 674 | expect: 675 | safeToString(str) == tostr 676 | 677 | where: 678 | str | tostr 679 | 'test' | 'test' 680 | null | 'null' 681 | } 682 | 683 | def "generate a Fibonacci method using alternate go to: syntax"() { 684 | def shell = new GroovyShell() 685 | def fib = shell.evaluate(""" 686 | @groovyx.ast.bytecode.Bytecode 687 | int fib(int n) { 688 | l0: 689 | iload 1 690 | iconst_2 691 | if_icmpge l1 692 | iload 1 693 | go to: l2 694 | l1: 695 | aload 0 696 | iload 1 697 | iconst_2 698 | isub 699 | invokevirtual '.fib','(I)I' 700 | aload 0 701 | iload 1 702 | iconst_1 703 | isub 704 | invokevirtual '.fib', '(I)I' 705 | iadd 706 | l2: 707 | ireturn 708 | } 709 | this.&fib 710 | """) 711 | 712 | expect: 713 | fib(i) == reference 714 | 715 | where: 716 | i | reference 717 | 0 | 0 718 | 1 | 1 719 | 2 | 1 720 | 3 | 2 721 | 4 | 3 722 | 5 | 5 723 | 6 | 8 724 | 7 | 13 725 | 8 | 21 726 | 9 | 34 727 | 10 | 55 728 | } 729 | 730 | 731 | private static sizeOf2dLevel(int[][] arr) { 732 | arr.length==0?0:arr[0].length 733 | } 734 | } 735 | -------------------------------------------------------------------------------- /src/test/groovy/groovyx/ast/bytecode/FieldsBytecodeSpock.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 C�dric Champeau 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 | * http://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 | package groovyx.ast.bytecode 22 | 23 | import spock.lang.Specification 24 | 25 | /** 26 | * Tests various access to fields using bytecode. 27 | */ 28 | class FieldsBytecodeSpock extends Specification { 29 | 30 | def "should set and get field value"() { 31 | def shell = new GroovyShell() 32 | def result = shell.evaluate(""" 33 | class Test { 34 | private String field; 35 | 36 | @groovyx.ast.bytecode.Bytecode 37 | public String getField() { 38 | aload_0 39 | getfield '.field','Ljava/lang/String;' 40 | areturn 41 | } 42 | 43 | @groovyx.ast.bytecode.Bytecode 44 | public void setField(String field) { 45 | aload_0 46 | aload_1 47 | putfield '.field','Ljava/lang/String;' 48 | return 49 | } 50 | } 51 | def test = new Test() 52 | test.field = "$value" 53 | test.field 54 | """) 55 | 56 | expect: 57 | result == value 58 | 59 | where: 60 | value << ['test','test2'] 61 | } 62 | 63 | def "should set and get static field value"() { 64 | def shell = new GroovyShell() 65 | def result = shell.evaluate(""" 66 | class Test { 67 | private static String field; 68 | 69 | @groovyx.ast.bytecode.Bytecode 70 | public String getField() { 71 | getstatic '.field','Ljava/lang/String;' 72 | areturn 73 | } 74 | 75 | @groovyx.ast.bytecode.Bytecode 76 | public void setField(String field) { 77 | aload_1 78 | putstatic '.field','Ljava/lang/String;' 79 | return 80 | } 81 | } 82 | def test = new Test() 83 | test.field = "$value" 84 | test.field 85 | """) 86 | 87 | expect: 88 | result == value 89 | 90 | where: 91 | value << ['test','test2'] 92 | } 93 | 94 | def "should set and get static field value with improved syntax"() { 95 | def shell = new GroovyShell() 96 | def result = shell.evaluate(""" 97 | class Test { 98 | private static int field; 99 | 100 | @groovyx.ast.bytecode.Bytecode 101 | public int getField() { 102 | getstatic field >> int 103 | ireturn 104 | } 105 | 106 | @groovyx.ast.bytecode.Bytecode 107 | public void setField(int field) { 108 | iload_1 109 | putstatic field >> int 110 | return 111 | } 112 | } 113 | def test = new Test() 114 | test.field = $value 115 | test.field 116 | """) 117 | 118 | expect: 119 | result == value 120 | 121 | where: 122 | value << (0..10) 123 | } 124 | 125 | def "should set and get static Integer field value with improved syntax"() { 126 | def shell = new GroovyShell() 127 | def result = shell.evaluate(""" 128 | class Test { 129 | private static Integer field; 130 | 131 | @groovyx.ast.bytecode.Bytecode 132 | public Integer getField() { 133 | getstatic field >> Integer 134 | areturn 135 | } 136 | 137 | @groovyx.ast.bytecode.Bytecode 138 | public void setField(Integer field) { 139 | aload_1 140 | putstatic field >> Integer 141 | return 142 | } 143 | } 144 | def test = new Test() 145 | test.field = $value 146 | test.field 147 | """) 148 | 149 | expect: 150 | result == value 151 | 152 | where: 153 | value << (0..10) 154 | } 155 | 156 | def "set a public field on an instance of a class with the groovyfied notation"() { 157 | def shell = new GroovyShell() 158 | def result = shell.evaluate(""" 159 | import groovyx.ast.bytecode.* 160 | 161 | @Bytecode 162 | def getPerson() { 163 | _new "groovyx/ast/bytecode/SomePerson" 164 | dup 165 | invokespecial SomePerson.""() >> void 166 | astore 1 167 | aload 1 168 | ldc "Guillaume" 169 | putfield SomePerson.name >> String 170 | aload 1 171 | areturn 172 | } 173 | getPerson().name 174 | """) 175 | 176 | expect: 177 | result == "Guillaume" 178 | } 179 | 180 | def "get a public static field with the groovyfied notation"() { 181 | def shell = new GroovyShell() 182 | def result = shell.evaluate(""" 183 | import groovyx.ast.bytecode.* 184 | 185 | class Someone { 186 | public static int age = 33 187 | 188 | @Bytecode 189 | int getAge() { 190 | getstatic age >> int 191 | ireturn 192 | } 193 | } 194 | 195 | new Someone().getAge() 196 | """) 197 | 198 | expect: 199 | result == 33 200 | } 201 | } 202 | 203 | class SomePerson { 204 | public String name 205 | public static int age = 33 206 | } -------------------------------------------------------------------------------- /src/test/groovy/groovyx/ast/bytecode/IndyBytecodeSpock.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * 4 | * * Copyright 2014 C�dric Champeau 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 | * * http://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 | package groovyx.ast.bytecode 22 | 23 | import org.codehaus.groovy.control.CompilerConfiguration 24 | import spock.lang.Specification 25 | 26 | /** 27 | * Unit tests for invokedynamic enabled bytecode 28 | */ 29 | 30 | class IndyBytecodeSpock extends Specification { 31 | 32 | def "delegate to a Fibonacci method using invokedynamic"() { 33 | given: 34 | def config = new CompilerConfiguration() 35 | config.optimizationOptions.indy = true 36 | def shell = new GroovyShell(config) 37 | def fib = shell.evaluate("""import groovy.transform.CompileStatic 38 | 39 | import java.lang.invoke.CallSite 40 | import java.lang.invoke.ConstantCallSite 41 | import java.lang.invoke.MethodHandles 42 | import java.lang.invoke.MethodType 43 | import static groovyjarjarasm.asm.Opcodes.* 44 | 45 | @CompileStatic 46 | class Helper { 47 | public static CallSite bootstrap(MethodHandles.Lookup lookup, String callType, MethodType type) { 48 | def mh = lookup.findStatic(Helper, 'fib', type) 49 | new ConstantCallSite(mh) 50 | } 51 | 52 | static int fib(int n) { n<2?n:fib(n-2)+fib(n-1) } 53 | } 54 | 55 | @groovyx.ast.bytecode.Bytecode 56 | int fib(int n) { 57 | ILOAD_1 58 | invokedynamic 'experiment', '(I)I', [H_INVOKESTATIC, 'Helper', 'bootstrap', [CallSite, MethodHandles.Lookup, String, MethodType]] 59 | ireturn 60 | } 61 | this.&fib 62 | """) 63 | expect: 64 | fib(i) == reference 65 | 66 | where: 67 | i | reference 68 | 0 | 0 69 | 1 | 1 70 | 2 | 1 71 | 3 | 2 72 | 4 | 3 73 | 5 | 5 74 | 6 | 8 75 | 7 | 13 76 | 8 | 21 77 | 9 | 34 78 | 10 | 55 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/test/groovy/groovyx/ast/bytecode/InstructionsImplementedSpock.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 C�dric Champeau 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 | * http://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 | 22 | 23 | package groovyx.ast.bytecode 24 | 25 | import spock.lang.Specification 26 | import org.codehaus.groovy.control.MultipleCompilationErrorsException 27 | import org.codehaus.groovy.control.ErrorCollector 28 | import org.codehaus.groovy.control.CompilerConfiguration 29 | 30 | /** 31 | * Created by IntelliJ IDEA. 32 | * User: cedric 33 | * Date: 15/01/11 34 | * Time: 22:38 35 | */ 36 | 37 | /** 38 | * A specification which simplifies determining which instructions 39 | * are converted by the AST Transformation. 40 | */ 41 | class InstructionsImplementedSpock extends Specification { 42 | def "all those no-arg instructions should be interpreted by the AST transform"() { 43 | def shell = new GroovyShell() 44 | 45 | when: 46 | try { 47 | shell.evaluate(""" 48 | @groovyx.ast.bytecode.Bytecode 49 | void test() { 50 | $instruction 51 | } 52 | """) 53 | } catch (java.lang.VerifyError err) { 54 | // not a problem, that's not what we're testing 55 | } 56 | then: 57 | notThrown(MultipleCompilationErrorsException) 58 | where: 59 | instruction << [ 60 | "aaload", 61 | "aastore", 62 | "aconst_null", 63 | "aload_0","aload_1","aload_2","aload_3", 64 | "areturn", 65 | "arraylength", 66 | "astore_0","astore_1","astore_2","astore_3", 67 | "athrow", 68 | "baload","bastore", 69 | "caload","castore", 70 | "d2f","d2i","d2l","dadd", 71 | "daload","dastore", 72 | "dcmpg","dcmpl", 73 | "dconst_0","dconst_1","ddiv","dload", 74 | "dload_0","dload_1","dload_2","dload_3", 75 | "dmul","dneg","drem","dreturn", 76 | "dstore_0","dstore_1","dstore_2","dstore_3", 77 | "dsub","dup","dup2","dup2_x1","dup2_x2","dup_x1","dup_x2", 78 | "f2d","f2i","f2l","fadd","faload","fastore", 79 | "fcmpg","fcmpl", 80 | "fconst_0","fconst_1","fconst_2", 81 | "fdiv","fload","fload_0","fload_1","fload_2","fload_3", 82 | "fmul","fneg","frem","freturn","fstore", 83 | "fstore_0","fstore_1","fstore_2","fstore_3", 84 | "fsub","i2b","i2c","i2d","i2f","i2l","i2s", 85 | "iadd","iaload","iand","iastore", 86 | "iconst_0","iconst_1","iconst_2","iconst_3","iconst_4","iconst_5","iconst_m1", 87 | "idiv", 88 | "iload_0","iload_1","iload_2","iload_3", 89 | "imul","ineg","ior","irem","ireturn", 90 | "ishl","ishr", 91 | "istore_0","istore_1","istore_2","istore_3", 92 | "isub","iushr","ixor", 93 | "l2d","l2f","l2i","ladd", 94 | "laload", 95 | "land","lastore","lcmp", 96 | "lconst_0","lconst_1", 97 | "ldiv", 98 | "lload_0","lload_1","lload_2","lload_3", 99 | "lmul","lneg","lor","lrem", 100 | "lreturn", 101 | "lshl","lshr", 102 | "lstore_0","lstore_1","lstore_2","lstore_3", 103 | "lsub","lushr","lxor", 104 | "monitorenter","monitorexit", 105 | "nop","pop","pop2", 106 | "vreturn", // should be "return" 107 | "saload","sastore", 108 | "swap" 109 | ] 110 | } 111 | 112 | def "all those one-arg instructions should be interpreted by the AST transform"() { 113 | def shell = new GroovyShell() 114 | def errorCollector = null 115 | def otherError = null 116 | when: 117 | try { 118 | shell.evaluate(""" 119 | @groovyx.ast.bytecode.Bytecode 120 | void test() { 121 | $instruction 'arg' 122 | } 123 | throw new org.codehaus.groovy.control.MultipleCompilationErrorsException() 124 | """) 125 | } catch (java.lang.VerifyError err) { 126 | // not a problem, that's not what we're testing 127 | } catch (MultipleCompilationErrorsException e) { 128 | errorCollector = e.errorCollector 129 | } catch (Throwable e) { 130 | otherError = e 131 | println "Error on [$instruction]" 132 | e.printStackTrace() 133 | } 134 | then: 135 | isSupported(instruction,errorCollector) 136 | otherError == null 137 | where: 138 | instruction << Instructions.UNARY_OPS 139 | } 140 | 141 | private boolean isSupported(instruction,ErrorCollector errorCollector) { 142 | errorCollector==null||errorCollector.errorCount==0 || !errorCollector.errors[0].cause.message.startsWith('Bytecode operation unsupported') 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/test/groovy/groovyx/ast/bytecode/PrimitiveTypesBytecodeSpock.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 C�dric Champeau 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 | * http://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 | package groovyx.ast.bytecode 22 | 23 | import spock.lang.Specification 24 | 25 | /** 26 | * Created by IntelliJ IDEA. 27 | * User: cedric 28 | * Date: 14/01/11 29 | * Time: 22:10 30 | */ 31 | 32 | class PrimitiveTypesBytecodeSpock extends Specification { 33 | 34 | def "test double square"() { 35 | def shell = new GroovyShell() 36 | def square = shell.evaluate(""" 37 | 38 | @groovyx.ast.bytecode.Bytecode 39 | double square(double x) { 40 | l0 41 | dload 1 42 | dload 1 43 | dmul 44 | dreturn 45 | } 46 | this.&square 47 | """) 48 | 49 | expect: 50 | square(x) == x*x 51 | 52 | where: 53 | x << (0..10) 54 | } 55 | 56 | def "test int square"() { 57 | def shell = new GroovyShell() 58 | def square = shell.evaluate(""" 59 | 60 | @groovyx.ast.bytecode.Bytecode 61 | int square(int x) { 62 | l0 63 | iload 1 64 | iload 1 65 | imul 66 | ireturn 67 | } 68 | this.&square 69 | """) 70 | 71 | expect: 72 | square(x) == x*x 73 | 74 | where: 75 | x << (0..10) 76 | } 77 | 78 | def "test float square"() { 79 | def shell = new GroovyShell() 80 | def square = shell.evaluate(""" 81 | 82 | @groovyx.ast.bytecode.Bytecode 83 | float square(float x) { 84 | l0 85 | fload 1 86 | fload 1 87 | fmul 88 | freturn 89 | } 90 | this.&square 91 | """) 92 | 93 | expect: 94 | square(x) == x*x 95 | 96 | where: 97 | x << (0..10) 98 | } 99 | 100 | def "test array creation with legacy syntax"() { 101 | def shell = new GroovyShell() 102 | def makeArray = shell.evaluate(""" 103 | @groovyx.ast.bytecode.Bytecode 104 | public Object[] makeArray(int a) { 105 | iload 1 106 | anewarray 'java/lang/Object' 107 | areturn 108 | } 109 | this.&makeArray 110 | """) 111 | 112 | expect: 113 | makeArray(size).length == size 114 | 115 | where: 116 | size << (0..10) 117 | } 118 | 119 | def "test array creation with groovified syntax"() { 120 | def shell = new GroovyShell() 121 | def makeArray = shell.evaluate(""" 122 | @groovyx.ast.bytecode.Bytecode 123 | public Object[] makeArray(int a) { 124 | iload 1 125 | anewarray Object 126 | areturn 127 | } 128 | this.&makeArray 129 | """) 130 | 131 | expect: 132 | makeArray(size).length == size 133 | 134 | where: 135 | size << (0..10) 136 | } 137 | 138 | def "test bidimensional array creation with legacy syntax"() { 139 | def shell = new GroovyShell() 140 | def makeArray = shell.evaluate(""" 141 | @groovyx.ast.bytecode.Bytecode 142 | public Object[][] makeArray(int a, int b) { 143 | iload 1 144 | iload 2 145 | multianewarray '[[Ljava/lang/Object;',2 146 | areturn 147 | } 148 | this.&makeArray 149 | """) 150 | 151 | expect: 152 | makeArray(size,size).length == size 153 | 154 | where: 155 | size << (0..10) 156 | } 157 | 158 | def "test bidimensional array creation with groovified syntax"() { 159 | def shell = new GroovyShell() 160 | def makeArray = shell.evaluate(""" 161 | @groovyx.ast.bytecode.Bytecode 162 | public Object[][] makeArray(int a, int b) { 163 | iload 1 164 | iload 2 165 | multianewarray Object[][],2 166 | areturn 167 | } 168 | this.&makeArray 169 | """) 170 | 171 | expect: 172 | makeArray(size,size).length == size 173 | 174 | where: 175 | size << (0..10) 176 | } 177 | 178 | def "test primitive int array creation with groovified syntax"() { 179 | def shell = new GroovyShell() 180 | def makeArray = shell.evaluate(""" 181 | @groovyx.ast.bytecode.Bytecode 182 | public int[] makeArray(int a) { 183 | iload 1 184 | newarray int 185 | areturn 186 | } 187 | this.&makeArray 188 | """) 189 | 190 | expect: 191 | makeArray(size).length == size 192 | 193 | where: 194 | size << (0..10) 195 | } 196 | 197 | def "test primitive bidimensional int array creation with groovified syntax"() { 198 | def shell = new GroovyShell() 199 | def makeArray = shell.evaluate(""" 200 | @groovyx.ast.bytecode.Bytecode 201 | public int[][] makeArray(int a) { 202 | iload 1 203 | anewarray int[] 204 | areturn 205 | } 206 | this.&makeArray 207 | """) 208 | 209 | expect: 210 | makeArray(size).length == size 211 | 212 | where: 213 | size << (0..10) 214 | } 215 | 216 | 217 | def "test long square"() { 218 | def shell = new GroovyShell() 219 | def square = shell.evaluate(""" 220 | 221 | @groovyx.ast.bytecode.Bytecode 222 | long square(long x) { 223 | l0 224 | lload 1 225 | lload 1 226 | lmul 227 | lreturn 228 | } 229 | this.&square 230 | """) 231 | 232 | expect: 233 | square(x) == x*x 234 | 235 | where: 236 | x << (0..10) 237 | } 238 | 239 | def "should sum array"() { 240 | def shell = new GroovyShell() 241 | def sum = shell.evaluate(""" 242 | @groovyx.ast.bytecode.Bytecode 243 | int sum() { 244 | l0 245 | bipush 10 246 | newarray t_int 247 | astore 1 248 | l1 249 | iconst_0 250 | istore 2 251 | l2 252 | iload 2 253 | aload 1 254 | arraylength 255 | if_icmpge l3 256 | l4 257 | aload 1 258 | iload 2 259 | iload 2 260 | iastore 261 | l5 262 | iinc 2,1 263 | _goto l2 264 | l3 265 | iconst_0 266 | istore 2 267 | l6 268 | aload 1 269 | astore 3 270 | l7 271 | aload 3 272 | arraylength 273 | istore 4 274 | l8 275 | iconst_0 276 | istore 5 277 | l9 278 | iload 5 279 | iload 4 280 | if_icmpge l10 281 | aload 3 282 | iload 5 283 | iaload 284 | istore 6 285 | l11 286 | iload 2 287 | iload 6 288 | iadd 289 | istore 2 290 | l12 291 | iinc 5,1 292 | _goto l9 293 | l10 294 | iload 2 295 | ireturn 296 | } 297 | sum() 298 | """) 299 | 300 | expect: 301 | sum == 45 302 | 303 | } 304 | 305 | def "sums the values of an int array with Groovified syntax and boxing"() { 306 | def shell = new GroovyShell() 307 | def sum = shell.evaluate(""" 308 | @groovyx.ast.bytecode.Bytecode 309 | public Integer sum(int[] a) { 310 | aload 1 311 | ifnonnull l0 312 | iconst_0 313 | invokestatic Integer.valueOf(int) >> Integer 314 | areturn 315 | l0: 316 | iconst_0 317 | istore 2 318 | aload 1 319 | astore 3 320 | aload 3 321 | arraylength 322 | istore 4 323 | iconst_0 324 | istore 5 325 | l1: 326 | iload 5 327 | iload 4 328 | if_icmpge l2 329 | aload 3 330 | iload 5 331 | iaload 332 | istore 6 333 | iload 2 334 | iload 6 335 | iadd 336 | istore 2 337 | iinc 5,1 338 | _goto l1 339 | l2: 340 | iload 2 341 | invokestatic Integer.valueOf(int) >> Integer 342 | areturn 343 | } 344 | this.&sum 345 | """ 346 | ) 347 | 348 | expect: 349 | sum(arr as int[]) == arr.sum() 350 | 351 | where: 352 | arr << [[1,1,1],[1,2,3]] 353 | } 354 | 355 | def "sums the values of an Integer array with Groovified syntax and unboxing"() { 356 | def shell = new GroovyShell() 357 | def sum = shell.evaluate(""" 358 | @groovyx.ast.bytecode.Bytecode 359 | public int sum(Integer[] a) { 360 | aload 1 361 | ifnonnull l0 362 | iconst_0 363 | ireturn 364 | l0: 365 | iconst_0 366 | istore 2 367 | aload 1 368 | astore 3 369 | aload 3 370 | arraylength 371 | istore 4 372 | iconst_0 373 | istore 5 374 | l1: 375 | iload 5 376 | iload 4 377 | if_icmpge l2 378 | aload 3 379 | iload 5 380 | aaload 381 | invokevirtual Integer.intValue() >> int 382 | istore 6 383 | iload 2 384 | iload 6 385 | iadd 386 | istore 2 387 | iinc 5,1 388 | _goto l1 389 | l2: 390 | iload 2 391 | ireturn 392 | } 393 | this.&sum 394 | """ 395 | ) 396 | 397 | expect: 398 | sum(arr as Integer[]) == arr.sum() 399 | 400 | where: 401 | arr << [[1,1,1],[1,2,3]] 402 | } 403 | } 404 | 405 | -------------------------------------------------------------------------------- /src/test/groovy/groovyx/ast/bytecode/TypeCastingBytecodeSpock.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Copyright 2011 C�dric Champeau 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 | * http://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 | package groovyx.ast.bytecode 22 | 23 | import spock.lang.Specification 24 | 25 | /** 26 | * Created by IntelliJ IDEA. 27 | * User: cedric 28 | * Date: 15/01/11 29 | * Time: 11:36 30 | */ 31 | class TypeCastingBytecodeSpock extends Specification { 32 | def "should cast object as string"() { 33 | def shell = new GroovyShell() 34 | def cast = shell.evaluate(""" 35 | @groovyx.ast.bytecode.Bytecode 36 | CharSequence cast(Object o) { 37 | aload 1 38 | checkcast 'java/lang/CharSequence' 39 | areturn 40 | } 41 | this.&cast 42 | """) 43 | 44 | expect: 45 | cast(o) == o 46 | 47 | where: 48 | o << ['String',new StringBuffer('test'), new StringBuilder('Spock')] 49 | } 50 | 51 | def "should cast object as string with groovified syntax"() { 52 | def shell = new GroovyShell() 53 | def cast = shell.evaluate(""" 54 | @groovyx.ast.bytecode.Bytecode 55 | CharSequence cast(Object o) { 56 | aload 1 57 | checkcast CharSequence 58 | areturn 59 | } 60 | this.&cast 61 | """) 62 | 63 | expect: 64 | cast(o) == o 65 | 66 | where: 67 | o << ['String',new StringBuffer('test'), new StringBuilder('Spock')] 68 | } 69 | 70 | def "should fail casting as char sequence"() { 71 | def shell = new GroovyShell() 72 | def cast = shell.evaluate(""" 73 | @groovyx.ast.bytecode.Bytecode 74 | CharSequence cast(Object o) { 75 | aload 1 76 | checkcast 'java/lang/CharSequence' 77 | areturn 78 | } 79 | this.&cast 80 | """) 81 | when: 82 | cast(x) 83 | 84 | then: 85 | thrown(ClassCastException) 86 | 87 | where: 88 | x << [1, 1f, 1d, new Object()] 89 | } 90 | 91 | def "should cast double as int"() { 92 | def shell = new GroovyShell() 93 | def cast = shell.evaluate(""" 94 | @groovyx.ast.bytecode.Bytecode 95 | int cast(double x) { 96 | dload 1 97 | d2i 98 | ireturn 99 | } 100 | this.&cast 101 | """) 102 | 103 | expect: 104 | cast(x) == (int) x 105 | 106 | where: 107 | x << [0d,1d,1.5d,1.9d, 2.1d, Double.MAX_VALUE] 108 | 109 | } 110 | 111 | def "should cast double as long"() { 112 | def shell = new GroovyShell() 113 | def cast = shell.evaluate(""" 114 | @groovyx.ast.bytecode.Bytecode 115 | long cast(double x) { 116 | dload 1 117 | d2l 118 | lreturn 119 | } 120 | this.&cast 121 | """) 122 | 123 | expect: 124 | cast(x) == (long) x 125 | 126 | where: 127 | x << [0d,1d,1.5d,1.9d, 2.1d, Double.MAX_VALUE] 128 | 129 | } 130 | 131 | def "should cast double as short"() { 132 | def shell = new GroovyShell() 133 | def cast = shell.evaluate(""" 134 | @groovyx.ast.bytecode.Bytecode 135 | short cast(double x) { 136 | dload 1 137 | d2i 138 | i2s 139 | ireturn 140 | } 141 | this.&cast 142 | """) 143 | 144 | expect: 145 | cast(x) == (short)x 146 | 147 | where: 148 | x << [0d,1d,1.5d,1.9d, 2.1d, Double.MAX_VALUE] 149 | 150 | } 151 | 152 | def "should cast double as byte"() { 153 | def shell = new GroovyShell() 154 | def cast = shell.evaluate(""" 155 | @groovyx.ast.bytecode.Bytecode 156 | byte cast(double x) { 157 | dload 1 158 | d2i 159 | i2b 160 | ireturn 161 | } 162 | this.&cast 163 | """) 164 | 165 | expect: 166 | cast(x) == (byte)x 167 | 168 | where: 169 | x << [0d,1d,1.5d,1.9d, 2.1d, Double.MAX_VALUE] 170 | } 171 | 172 | def "should cast double as char"() { 173 | def shell = new GroovyShell() 174 | def cast = shell.evaluate(""" 175 | @groovyx.ast.bytecode.Bytecode 176 | char cast(double x) { 177 | dload 1 178 | d2i 179 | i2c 180 | ireturn 181 | } 182 | this.&cast 183 | """) 184 | 185 | expect: 186 | cast(x) == (char)x 187 | 188 | where: 189 | x << [0d,1d,1.5d,1.9d, 2.1d, Double.MAX_VALUE] 190 | } 191 | 192 | def "should cast byte as double"() { 193 | def shell = new GroovyShell() 194 | def cast = shell.evaluate(""" 195 | @groovyx.ast.bytecode.Bytecode 196 | double cast(byte x) { 197 | iload 1 198 | i2d 199 | dreturn 200 | } 201 | this.&cast 202 | """) 203 | 204 | expect: 205 | cast(x) == (double)x 206 | 207 | where: 208 | x << [(byte)0,(byte)1,(byte)3] 209 | } 210 | 211 | def "should cast byte as int"() { 212 | def shell = new GroovyShell() 213 | def cast = shell.evaluate(""" 214 | @groovyx.ast.bytecode.Bytecode 215 | int cast(byte x) { 216 | iload 1 217 | ireturn 218 | } 219 | this.&cast 220 | """) 221 | 222 | expect: 223 | cast(x) == (double)x 224 | 225 | where: 226 | x << [(byte)0,(byte)1,(byte)3] 227 | } 228 | 229 | def "should cast short as int"() { 230 | def shell = new GroovyShell() 231 | def cast = shell.evaluate(""" 232 | @groovyx.ast.bytecode.Bytecode 233 | int cast(short x) { 234 | iload 1 235 | ireturn 236 | } 237 | this.&cast 238 | """) 239 | 240 | expect: 241 | cast(x) == (short)x 242 | 243 | where: 244 | x << [(short)(short)0,(short)1i,(short)3i, Short.MAX_VALUE] 245 | } 246 | 247 | def "should cast byte as long"() { 248 | def shell = new GroovyShell() 249 | def cast = shell.evaluate(""" 250 | @groovyx.ast.bytecode.Bytecode 251 | long cast(byte x) { 252 | iload 1 253 | i2l 254 | lreturn 255 | } 256 | this.&cast 257 | """) 258 | 259 | expect: 260 | cast(x) == (long)x 261 | 262 | where: 263 | x << [(byte)0,(byte)1,(byte)3] 264 | } 265 | 266 | def "should cast long as double"() { 267 | def shell = new GroovyShell() 268 | def cast = shell.evaluate(""" 269 | @groovyx.ast.bytecode.Bytecode 270 | double cast(long x) { 271 | lload 1 272 | l2d 273 | dreturn 274 | } 275 | this.&cast 276 | """) 277 | 278 | expect: 279 | cast(x) == (double)x 280 | 281 | where: 282 | x << [0l,1l,2l,3l, Long.MAX_VALUE] 283 | } 284 | 285 | def "should cast long as char"() { 286 | def shell = new GroovyShell() 287 | def cast = shell.evaluate(""" 288 | @groovyx.ast.bytecode.Bytecode 289 | char cast(long x) { 290 | lload 1 291 | l2i 292 | i2c 293 | ireturn 294 | } 295 | this.&cast 296 | """) 297 | 298 | expect: 299 | cast(x) == (char)x 300 | 301 | where: 302 | x << [0l,1l,2l,3l, Long.MAX_VALUE] 303 | } 304 | 305 | def "should cast char as double"() { 306 | def shell = new GroovyShell() 307 | def cast = shell.evaluate(""" 308 | @groovyx.ast.bytecode.Bytecode 309 | double cast(char x) { 310 | iload 1 311 | i2d 312 | dreturn 313 | } 314 | this.&cast 315 | """) 316 | 317 | expect: 318 | cast(x) == (double)x 319 | 320 | where: 321 | x << [(char)0,(char)1,(char)2,(char)3, Character.MAX_VALUE] 322 | } 323 | 324 | 325 | def "should cast float as double"() { 326 | def shell = new GroovyShell() 327 | def cast = shell.evaluate(""" 328 | @groovyx.ast.bytecode.Bytecode 329 | double cast(float x) { 330 | fload 1 331 | f2d 332 | dreturn 333 | } 334 | this.&cast 335 | """) 336 | 337 | expect: 338 | cast(x) == (double)x 339 | 340 | where: 341 | x << [0f,1f,1.5f,3f, Float.MAX_VALUE] 342 | } 343 | 344 | def "should cast double as float"() { 345 | def shell = new GroovyShell() 346 | def cast = shell.evaluate(""" 347 | @groovyx.ast.bytecode.Bytecode 348 | float cast(double x) { 349 | dload 1 350 | d2f 351 | freturn 352 | } 353 | this.&cast 354 | """) 355 | 356 | expect: 357 | cast(x) == (float)x 358 | 359 | where: 360 | x << [0d,1d,1.5d,3d, Double.MAX_VALUE] 361 | } 362 | 363 | def "tests instanceof with legacy syntax"() { 364 | def shell = new GroovyShell() 365 | def className = shell.evaluate(""" 366 | @groovyx.ast.bytecode.Bytecode 367 | public String className(Object a) { 368 | aload 1 369 | _instanceof 'java/lang/String' 370 | ifeq l0 371 | ldc "java.lang.String" 372 | areturn 373 | l0: 374 | aload 1 375 | invokevirtual 'java/lang/Object.getClass','()Ljava/lang/Class;' 376 | invokevirtual 'java/lang/Class.getCanonicalName','()Ljava/lang/String;' 377 | areturn 378 | } 379 | this.&className 380 | """) 381 | 382 | expect: 383 | className(o) == o.class.canonicalName 384 | 385 | where: 386 | o << ["aaa",new Integer(5),1i] 387 | } 388 | 389 | def "tests instanceof with Groovified syntax"() { 390 | def shell = new GroovyShell() 391 | def className = shell.evaluate(""" 392 | @groovyx.ast.bytecode.Bytecode 393 | public String className(Object a) { 394 | aload 1 395 | instance of: String 396 | ifeq l0 397 | ldc "java.lang.String" 398 | areturn 399 | l0: 400 | aload 1 401 | invokevirtual Object.getClass() >> Class 402 | invokevirtual Class.getCanonicalName() >> String 403 | areturn 404 | } 405 | this.&className 406 | """) 407 | 408 | expect: 409 | className(o) == o.class.canonicalName 410 | 411 | where: 412 | o << ["aaa",new Integer(5),1i] 413 | } 414 | } 415 | --------------------------------------------------------------------------------