1+ /*
2+ * Licensed to the Apache Software Foundation (ASF) under one
3+ * or more contributor license agreements. See the NOTICE file
4+ * distributed with this work for additional information
5+ * regarding copyright ownership. The ASF licenses this file
6+ * to you under the Apache License, Version 2.0 (the
7+ * "License"); you may not use this file except in compliance
8+ * with the License. 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,
13+ * software distributed under the License is distributed on an
14+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+ * KIND, either express or implied. See the License for the
16+ * specific language governing permissions and limitations
17+ * under the License.
18+ */
19+ package org .codehaus .groovy .transform .sc .transformers ;
20+
21+ import org .codehaus .groovy .ast .ClassHelper ;
22+ import org .codehaus .groovy .ast .ClassNode ;
23+ import org .codehaus .groovy .ast .GroovyCodeVisitor ;
24+ import org .codehaus .groovy .ast .InnerClassNode ;
25+ import org .codehaus .groovy .ast .MethodNode ;
26+ import org .codehaus .groovy .ast .expr .BinaryExpression ;
27+ import org .codehaus .groovy .ast .expr .BooleanExpression ;
28+ import org .codehaus .groovy .ast .expr .Expression ;
29+ import org .codehaus .groovy .ast .expr .ExpressionTransformer ;
30+ import org .codehaus .groovy .ast .expr .NotExpression ;
31+ import org .codehaus .groovy .classgen .AsmClassGenerator ;
32+ import org .codehaus .groovy .classgen .asm .BytecodeHelper ;
33+ import org .codehaus .groovy .classgen .asm .OperandStack ;
34+ import org .codehaus .groovy .classgen .asm .WriterController ;
35+ import org .codehaus .groovy .classgen .asm .sc .StaticTypesTypeChooser ;
36+ import org .codehaus .groovy .transform .stc .ExtensionMethodNode ;
37+ import groovyjarjarasm .asm .Label ;
38+ import groovyjarjarasm .asm .MethodVisitor ;
39+
40+ import java .lang .reflect .Modifier ;
41+ import java .util .Iterator ;
42+ import java .util .List ;
43+
44+ import static org .codehaus .groovy .transform .stc .StaticTypeCheckingSupport .findDGMMethodsByNameAndArguments ;
45+ import static groovyjarjarasm .asm .Opcodes .DCMPG ;
46+ import static groovyjarjarasm .asm .Opcodes .DCONST_0 ;
47+ import static groovyjarjarasm .asm .Opcodes .DUP ;
48+ import static groovyjarjarasm .asm .Opcodes .F2D ;
49+ import static groovyjarjarasm .asm .Opcodes .GOTO ;
50+ import static groovyjarjarasm .asm .Opcodes .ICONST_0 ;
51+ import static groovyjarjarasm .asm .Opcodes .IFNONNULL ;
52+ import static groovyjarjarasm .asm .Opcodes .INVOKEVIRTUAL ;
53+ import static groovyjarjarasm .asm .Opcodes .LCMP ;
54+ import static groovyjarjarasm .asm .Opcodes .LCONST_0 ;
55+ import static groovyjarjarasm .asm .Opcodes .POP ;
56+
57+ public class BooleanExpressionTransformer {
58+ private final StaticCompilationTransformer transformer ;
59+
60+ public BooleanExpressionTransformer (StaticCompilationTransformer staticCompilationTransformer ) {
61+ transformer = staticCompilationTransformer ;
62+ }
63+
64+ Expression transformBooleanExpression (final BooleanExpression booleanExpression ) {
65+ if (booleanExpression instanceof NotExpression ) {
66+ return transformer .superTransform (booleanExpression );
67+ }
68+ final Expression expression = booleanExpression .getExpression ();
69+ if (!(expression instanceof BinaryExpression )) {
70+ StaticTypesTypeChooser typeChooser = transformer .getTypeChooser ();
71+ final ClassNode type = typeChooser .resolveType (expression , transformer .getClassNode ());
72+ BooleanExpression transformed = new OptimizingBooleanExpression (transformer .transform (expression ),type );
73+ transformed .setSourcePosition (booleanExpression );
74+ transformed .copyNodeMetaData (booleanExpression );
75+ return transformed ;
76+ }
77+ return transformer .superTransform (booleanExpression );
78+ }
79+
80+ private static boolean isExtended (ClassNode owner , Iterator <InnerClassNode > classes ) {
81+ while (classes .hasNext ()) {
82+ InnerClassNode next = classes .next ();
83+ if (next !=owner && next .isDerivedFrom (owner )) return true ;
84+ // GRECLIPSE add
85+ if (isExtended (owner ,next .getInnerClasses ())) return true ;
86+ // GRECLIPSE end
87+ }
88+ /* GRECLIPSE edit -- GROOVY-10424
89+ if (owner.getInnerClasses().hasNext()) {
90+ return isExtended(owner, owner.getInnerClasses());
91+ }
92+ */
93+ return false ;
94+ }
95+
96+ private static class OptimizingBooleanExpression extends BooleanExpression {
97+
98+ private final Expression expression ;
99+ private final ClassNode type ;
100+
101+ public OptimizingBooleanExpression (final Expression expression , final ClassNode type ) {
102+ super (expression );
103+ this .expression = expression ;
104+ // we must use the redirect node, otherwise InnerClassNode would not have the "correct" type
105+ this .type = type .redirect ();
106+ }
107+
108+ @ Override
109+ public Expression transformExpression (final ExpressionTransformer transformer ) {
110+ Expression ret = new OptimizingBooleanExpression (transformer .transform (expression ), type );
111+ ret .setSourcePosition (this );
112+ ret .copyNodeMetaData (this );
113+ return ret ;
114+ }
115+
116+ @ Override
117+ public void visit (final GroovyCodeVisitor visitor ) {
118+ if (visitor instanceof AsmClassGenerator ) {
119+ AsmClassGenerator acg = (AsmClassGenerator ) visitor ;
120+ WriterController controller = acg .getController ();
121+ OperandStack os = controller .getOperandStack ();
122+
123+ if (type .equals (ClassHelper .boolean_TYPE )) {
124+ expression .visit (visitor );
125+ os .doGroovyCast (ClassHelper .boolean_TYPE );
126+ return ;
127+ }
128+ if (type .equals (ClassHelper .Boolean_TYPE )) {
129+ MethodVisitor mv = controller .getMethodVisitor ();
130+ expression .visit (visitor );
131+ Label unbox = new Label ();
132+ Label exit = new Label ();
133+ // check for null
134+ mv .visitInsn (DUP );
135+ mv .visitJumpInsn (IFNONNULL , unbox );
136+ mv .visitInsn (POP );
137+ mv .visitInsn (ICONST_0 );
138+ mv .visitJumpInsn (GOTO , exit );
139+ mv .visitLabel (unbox );
140+ // unbox
141+ // GROOVY-6270
142+ if (!os .getTopOperand ().equals (type )) BytecodeHelper .doCast (mv , type );
143+ mv .visitMethodInsn (INVOKEVIRTUAL , "java/lang/Boolean" , "booleanValue" , "()Z" , false );
144+ mv .visitLabel (exit );
145+ os .replace (ClassHelper .boolean_TYPE );
146+ return ;
147+ }
148+ ClassNode top = type ;
149+ if (ClassHelper .isPrimitiveType (top )) {
150+ expression .visit (visitor );
151+ // in case of null safe invocation, it is possible that what was supposed to be a primitive type
152+ // becomes the "null" constant, so we need to recheck
153+ top = controller .getOperandStack ().getTopOperand ();
154+ if (ClassHelper .isPrimitiveType (top )) {
155+ if (top .equals (ClassHelper .int_TYPE ) || top .equals (ClassHelper .byte_TYPE )
156+ || top .equals (ClassHelper .short_TYPE ) || top .equals (ClassHelper .char_TYPE )) {
157+ // int on stack
158+ } else if (top .equals (ClassHelper .long_TYPE )) {
159+ MethodVisitor mv = controller .getMethodVisitor ();
160+ mv .visitInsn (LCONST_0 );
161+ mv .visitInsn (LCMP );
162+ controller .getOperandStack ().replace (ClassHelper .boolean_TYPE );
163+ } else if (top .equals (ClassHelper .float_TYPE )) {
164+ MethodVisitor mv = controller .getMethodVisitor ();
165+ mv .visitInsn (F2D );
166+ mv .visitInsn (DCONST_0 );
167+ mv .visitInsn (DCMPG );
168+ controller .getOperandStack ().replace (ClassHelper .boolean_TYPE );
169+ } else if (top .equals (ClassHelper .double_TYPE )) {
170+ MethodVisitor mv = controller .getMethodVisitor ();
171+ mv .visitInsn (DCONST_0 );
172+ mv .visitInsn (DCMPG );
173+ controller .getOperandStack ().replace (ClassHelper .boolean_TYPE );
174+ }
175+ return ;
176+ }
177+ }
178+ List <MethodNode > asBoolean = findDGMMethodsByNameAndArguments (controller .getSourceUnit ().getClassLoader (), top , "asBoolean" , ClassNode .EMPTY_ARRAY );
179+ if (asBoolean .size () == 1 ) {
180+ MethodNode node = asBoolean .get (0 );
181+ if (node instanceof ExtensionMethodNode ) {
182+ MethodNode dgmNode = ((ExtensionMethodNode ) node ).getExtensionMethodNode ();
183+ ClassNode owner = dgmNode .getParameters ()[0 ].getType ();
184+ if (ClassHelper .OBJECT_TYPE .equals (owner )) {
185+ // we may inline a var!=null check instead of calling a helper method iff
186+ // (1) the class doesn't define an asBoolean method (already tested)
187+ // (2) no subclass defines an asBoolean method
188+ // For (2), we check that we are in one of those cases
189+ // (a) a final class
190+ // (b) a private inner class without subclass
191+ if (Modifier .isFinal (top .getModifiers ())
192+ || (top instanceof InnerClassNode
193+ && Modifier .isPrivate (top .getModifiers ())
194+ && !isExtended (top , top .getOuterClass ().getInnerClasses ()))
195+ ) {
196+ CompareToNullExpression expr = new CompareToNullExpression (
197+ expression , false
198+ );
199+ expr .visit (acg );
200+ return ;
201+ }
202+ }
203+ }
204+ }
205+ super .visit (visitor );
206+ } else {
207+ super .visit (visitor );
208+ }
209+ }
210+ }
211+ }
0 commit comments