001/* 002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package gololang.ir; 012 013import java.util.Objects; 014import java.util.LinkedList; 015import java.util.List; 016 017/** 018 * A {@code try catch finally} statement. 019 * 020 * <p>Represents nodes such as: 021 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 022 * try { 023 * # try block 024 * } catch (ex) { 025 * # catch block 026 * } finally { 027 * # finally block 028 * } 029 * </code></pre> 030 */ 031public final class TryCatchFinally extends GoloStatement<TryCatchFinally> { 032 033 public static final String DUMMY_TRY_RESULT_VARIABLE = "__$$_result"; 034 private static final String DUMMY_EXCEPTION_VARIABLE = "__$$_exception"; 035 private String exceptionId; 036 private LocalReference dummyException; 037 private Block tryBlock; 038 private Block catchBlock; 039 private Block finallyBlock; 040 041 private TryCatchFinally() { 042 super(); 043 this.exceptionId = null; 044 } 045 046 /** 047 * Creates an empty {@code try catch finally} statement. 048 */ 049 public static TryCatchFinally tryCatch() { 050 return new TryCatchFinally(); 051 } 052 053 054 /** 055 * Creates a complete {@code try catch finally} statement. 056 * <p> 057 * Less readable than the fluent API, but useful for meta-generation. 058 */ 059 public static TryCatchFinally create(String exceptionName, GoloElement<?> tryBlock, GoloElement<?> catchBlock, GoloElement<?> finallyBlock) { 060 return new TryCatchFinally() 061 .trying(tryBlock) 062 .catching(exceptionName, catchBlock) 063 .finalizing(finallyBlock); 064 } 065 066 protected TryCatchFinally self() { return this; } 067 068 /** 069 * Get the exception name. 070 * 071 * <p>{@code ex} in the previous example. 072 */ 073 public String getExceptionId() { 074 return exceptionId; 075 } 076 077 /** 078 * Returns the internal index of the exception reference. 079 */ 080 public int getExceptionRefIndex() { 081 if (!this.hasCatchBlock()) { 082 return this.dummyException.getIndex(); 083 } 084 return this.catchBlock.getReferenceTable().get(this.exceptionId).getIndex(); 085 } 086 087 public Block getTryBlock() { 088 return tryBlock; 089 } 090 091 /** 092 * Defines the try block. 093 * 094 * <p>This is a builder method. 095 * 096 * @param block a {@link Block} or a statement that will be wrapped. 097 * @see Block#of(Object) 098 */ 099 public TryCatchFinally trying(Object block) { 100 this.tryBlock = makeParentOf(Block.of(block)); 101 return this; 102 } 103 104 public Block getCatchBlock() { 105 return catchBlock != null ? catchBlock : Block.nullBlock(); 106 } 107 108 /** 109 * Defines the catch block. 110 * 111 * <p>This is a builder method. 112 * 113 * @param exceptionId the name of the catched exception instance. 114 * @param block a {@link Block} or a statement that will be wrapped. 115 * @see Block#of(Object) 116 */ 117 public TryCatchFinally catching(String exceptionId, Object block) { 118 this.exceptionId = exceptionId; 119 return catching(block); 120 } 121 122 public TryCatchFinally catching(Object block) { 123 if (block == null) { 124 this.catchBlock = null; 125 this.exceptionId = null; 126 return this; 127 } 128 this.catchBlock = makeParentOf(Block.of(block)); 129 this.catchBlock.getReferenceTable().add(LocalReference.of(exceptionId).synthetic()); 130 return this; 131 } 132 133 public Block getFinallyBlock() { 134 return finallyBlock != null ? finallyBlock : Block.nullBlock(); 135 } 136 137 /** 138 * Defines the finally block. 139 * 140 * <p>This is a builder method. 141 * 142 * @param block a {@link Block} or a statement that will be wrapped. 143 * @see Block#of(Object) 144 */ 145 public TryCatchFinally finalizing(Object block) { 146 this.dummyException = LocalReference.generate(DUMMY_EXCEPTION_VARIABLE).synthetic(); 147 this.finallyBlock = makeParentOf(Block.of(block)); 148 this.finallyBlock.getReferenceTable().add(this.dummyException); 149 return this; 150 } 151 152 public String dummyExceptionName() { 153 return this.dummyException.getName(); 154 } 155 156 public boolean hasFinallyBlock() { 157 return finallyBlock != null; 158 } 159 160 public boolean hasCatchBlock() { 161 return catchBlock != null; 162 } 163 164 /** 165 * Checks if a return is present in the try or catch block and a finally is present. 166 * <p> 167 * In this case we must not generate a return but a jump to the finally block. 168 */ 169 public boolean mustJumpToFinally() { 170 return (this.tryBlock.hasReturn() 171 || (this.catchBlock != null && this.catchBlock.hasReturn())) 172 && this.finallyBlock != null; 173 174 } 175 176 public boolean hasReturn() { 177 return (this.tryBlock.hasReturn() 178 && (this.catchBlock == null || this.catchBlock.hasReturn())) 179 || (this.finallyBlock != null 180 && this.finallyBlock.hasReturn()); 181 } 182 183 /** 184 * Check if this statement has both a catch block and a finally block. 185 */ 186 public boolean isTryCatchFinally() { 187 return hasCatchBlock() && hasFinallyBlock(); 188 } 189 190 /** 191 * Check if this statement has only a catch block. 192 */ 193 public boolean isTryCatch() { 194 return hasCatchBlock() && !hasFinallyBlock(); 195 } 196 197 /** 198 * Check if this statement has only a finally block. 199 */ 200 public boolean isTryFinally() { 201 return !hasCatchBlock() && hasFinallyBlock(); 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 @Override 208 public void accept(GoloIrVisitor visitor) { 209 visitor.visitTryCatchFinally(this); 210 } 211 212 /** 213 * {@inheritDoc} 214 */ 215 @Override 216 public List<GoloElement<?>> children() { 217 LinkedList<GoloElement<?>> children = new LinkedList<>(); 218 children.add(tryBlock); 219 if (catchBlock != null) { 220 children.add(catchBlock); 221 } 222 if (finallyBlock != null) { 223 children.add(finallyBlock); 224 } 225 return children; 226 } 227 228 /** 229 * {@inheritDoc} 230 */ 231 @Override 232 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 233 if (Objects.equals(original, tryBlock)) { 234 trying(newElement); 235 } else if (Objects.equals(original, catchBlock)) { 236 catching(newElement); 237 } else if (Objects.equals(original, finallyBlock)) { 238 finalizing(newElement); 239 } else { 240 throw cantReplace(original, newElement); 241 } 242 } 243}