or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

concurrency.mdconstant-value.mdcontracts.mdframework.mdindex-bounds.mdindex.mdnullness.mdoptional.mdresources.mdsignedness.mdstring-format.mdunits.md

resources.mddocs/

0

# Resource Management Annotations

1

2

Resource management annotations track object lifecycle and ensure proper cleanup of resources. These annotations help prevent resource leaks by tracking which methods must be called and which objects own resources.

3

4

## Capabilities

5

6

### Must-Call Annotations

7

8

Annotations that specify which methods must be called on objects before they are deallocated.

9

10

```java { .api }

11

/**

12

* Specifies methods that must be called on this object

13

*/

14

@Documented

15

@Retention(RetentionPolicy.RUNTIME)

16

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

17

@SubtypeOf(MustCallUnknown.class)

18

public @interface MustCall {

19

String[] value();

20

}

21

22

/**

23

* Creates an alias for must-call obligations

24

*/

25

@Documented

26

@Retention(RetentionPolicy.RUNTIME)

27

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER, ElementType.PARAMETER})

28

@SubtypeOf(MustCall.class)

29

public @interface MustCallAlias {}

30

31

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

32

@DefaultQualifierInHierarchy

33

public @interface MustCallUnknown {}

34

35

/**

36

* Polymorphic must-call qualifier

37

*/

38

@Documented

39

@Retention(RetentionPolicy.RUNTIME)

40

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

41

@PolymorphicQualifier(MustCallUnknown.class)

42

public @interface PolyMustCall {}

43

44

/**

45

* Inheritable must-call annotation for class hierarchies

46

*/

47

@Documented

48

@Retention(RetentionPolicy.RUNTIME)

49

@Target(ElementType.TYPE)

50

@InheritedAnnotation

51

public @interface InheritableMustCall {

52

String[] value();

53

}

54

```

55

56

**Usage Examples:**

57

58

```java

59

import org.checkerframework.checker.mustcall.qual.*;

60

import java.io.*;

61

62

// File must have close() called

63

@InheritableMustCall("close")

64

public class ManagedFile {

65

private FileInputStream stream;

66

67

public ManagedFile(String filename) throws IOException {

68

this.stream = new FileInputStream(filename);

69

}

70

71

public int read() throws IOException {

72

return stream.read();

73

}

74

75

public void close() throws IOException {

76

if (stream != null) {

77

stream.close();

78

stream = null;

79

}

80

}

81

}

82

83

public class ResourceExample {

84

public void processFile(String filename) throws IOException {

85

@MustCall("close") ManagedFile file = new ManagedFile(filename);

86

try {

87

int data = file.read();

88

System.out.println("Read: " + data);

89

} finally {

90

file.close(); // Required - must call close()

91

}

92

}

93

94

// Polymorphic method preserves must-call obligations

95

public @PolyMustCall ManagedFile processResource(@PolyMustCall ManagedFile resource) {

96

// Process the resource without changing its must-call obligation

97

return resource;

98

}

99

100

// Try-with-resources automatically handles must-call

101

public void safeProcessing(String filename) throws IOException {

102

try (@MustCall("close") ManagedFile file = new ManagedFile(filename)) {

103

int data = file.read();

104

System.out.println("Read: " + data);

105

} // close() called automatically

106

}

107

}

108

```

109

110

### Called Methods Tracking

111

112

Annotations that track which methods have been called on objects.

113

114

```java { .api }

115

/**

116

* Indicates which methods have been called on the object

117

*/

118

@Documented

119

@Retention(RetentionPolicy.RUNTIME)

120

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

121

@SubtypeOf(CalledMethodsBottom.class)

122

public @interface CalledMethods {

123

String[] value();

124

}

125

126

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

127

@SubtypeOf(CalledMethods.class)

128

public @interface CalledMethodsBottom {}

129

130

/**

131

* Predicate over called methods (for complex conditions)

132

*/

133

@Documented

134

@Retention(RetentionPolicy.RUNTIME)

135

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

136

public @interface CalledMethodsPredicate {

137

String value();

138

}

139

```

140

141

**Usage Examples:**

142

143

```java

144

import org.checkerframework.checker.calledmethods.qual.*;

145

146

public class DatabaseConnection {

147

private boolean connected = false;

148

private boolean authenticated = false;

149

150

public void connect() {

151

this.connected = true;

152

}

153

154

public void authenticate(String username, String password) {

155

if (!connected) {

156

throw new IllegalStateException("Must connect first");

157

}

158

this.authenticated = true;

159

}

160

161

public void executeQuery(String query) {

162

if (!connected || !authenticated) {

163

throw new IllegalStateException("Must connect and authenticate first");

164

}

165

System.out.println("Executing: " + query);

166

}

167

168

public void disconnect() {

169

this.connected = false;

170

this.authenticated = false;

171

}

172

}

173

174

public class CalledMethodsExample {

175

public void safeDatabase() {

176

DatabaseConnection db = new DatabaseConnection();

177

178

// Track method calls

179

db.connect();

180

@CalledMethods("connect") DatabaseConnection connectedDb = db;

181

182

db.authenticate("user", "pass");

183

@CalledMethods({"connect", "authenticate"}) DatabaseConnection authDb = db;

184

185

// Safe to execute query after both methods called

186

authDb.executeQuery("SELECT * FROM users");

187

188

db.disconnect();

189

}

190

191

// Method that requires specific methods to have been called

192

public void processConnectedDatabase(@CalledMethods("connect") DatabaseConnection db) {

193

// Can safely assume connect() has been called

194

db.authenticate("admin", "secret");

195

db.executeQuery("SELECT * FROM admin_table");

196

}

197

}

198

```

199

200

### Ownership Annotations

201

202

Annotations that track resource ownership and transfer.

203

204

```java { .api }

205

/**

206

* Parameter takes ownership of the resource

207

*/

208

@Documented

209

@Retention(RetentionPolicy.RUNTIME)

210

@Target(ElementType.PARAMETER)

211

public @interface Owning {}

212

213

/**

214

* Parameter does not take ownership of the resource

215

*/

216

@Documented

217

@Retention(RetentionPolicy.RUNTIME)

218

@Target(ElementType.PARAMETER)

219

public @interface NotOwning {}

220

```

221

222

**Usage Examples:**

223

224

```java

225

import org.checkerframework.checker.mustcall.qual.*;

226

import java.io.Closeable;

227

228

public class OwnershipExample {

229

// Method takes ownership and is responsible for closing

230

public void processAndClose(@Owning @MustCall("close") Closeable resource) {

231

try {

232

// Process the resource

233

System.out.println("Processing resource");

234

} finally {

235

try {

236

resource.close(); // Owner must close

237

} catch (Exception e) {

238

// Handle close error

239

}

240

}

241

}

242

243

// Method uses resource but doesn't take ownership

244

public void processOnly(@NotOwning @MustCall("close") Closeable resource) {

245

// Use the resource but don't close it

246

System.out.println("Using resource");

247

// Caller is still responsible for closing

248

}

249

250

public void example() throws Exception {

251

@MustCall("close") FileInputStream file = new FileInputStream("data.txt");

252

253

// Transfer ownership - file will be closed by processAndClose

254

processAndClose(file);

255

// Don't close file here - ownership was transferred

256

257

@MustCall("close") FileInputStream file2 = new FileInputStream("data2.txt");

258

try {

259

// Use without transferring ownership

260

processOnly(file2);

261

// Still responsible for closing since ownership not transferred

262

} finally {

263

file2.close();

264

}

265

}

266

}

267

```

268

269

### Side Effect Tracking

270

271

Annotations for tracking side effects that create or modify must-call obligations.

272

273

```java { .api }

274

/**

275

* Method creates must-call obligations for specified expressions

276

*/

277

@Target(ElementType.METHOD)

278

@Retention(RetentionPolicy.RUNTIME)

279

@InheritedAnnotation

280

public @interface CreatesMustCallFor {

281

String[] value();

282

}

283

```

284

285

**Usage Examples:**

286

287

```java

288

import org.checkerframework.checker.mustcall.qual.*;

289

import java.io.*;

290

291

public class ConnectionPool {

292

private List<@MustCall("close") Connection> activeConnections = new ArrayList<>();

293

294

@CreatesMustCallFor("this")

295

public Connection borrowConnection() {

296

@MustCall("close") Connection conn = new Connection();

297

activeConnections.add(conn);

298

return conn; // Creates must-call obligation for the pool

299

}

300

301

public void returnConnection(@MustCall("close") Connection conn) {

302

activeConnections.remove(conn);

303

// Connection is returned to pool, not closed yet

304

}

305

306

@MustCall("closeAll")

307

public void closeAll() {

308

for (@MustCall("close") Connection conn : activeConnections) {

309

try {

310

conn.close();

311

} catch (Exception e) {

312

// Log error

313

}

314

}

315

activeConnections.clear();

316

}

317

}

318

319

@MustCall("close")

320

class Connection {

321

private boolean closed = false;

322

323

public void execute(String command) {

324

if (closed) throw new IllegalStateException("Connection closed");

325

System.out.println("Executing: " + command);

326

}

327

328

public void close() {

329

this.closed = true;

330

}

331

}

332

333

public class SideEffectExample {

334

public void useConnectionPool() {

335

@MustCall("closeAll") ConnectionPool pool = new ConnectionPool();

336

337

// borrowConnection creates must-call obligation for pool

338

@MustCall("close") Connection conn1 = pool.borrowConnection();

339

@MustCall("close") Connection conn2 = pool.borrowConnection();

340

341

try {

342

conn1.execute("SELECT * FROM users");

343

conn2.execute("UPDATE settings SET value = 'new' WHERE key = 'config'");

344

} finally {

345

// Return connections to pool

346

pool.returnConnection(conn1);

347

pool.returnConnection(conn2);

348

349

// Pool must close all connections

350

pool.closeAll(); // Required due to @CreatesMustCallFor

351

}

352

}

353

}

354

```

355

356

### Method Contract Annotations

357

358

Annotations for expressing resource-related method contracts.

359

360

```java { .api }

361

/**

362

* Method postcondition: ensures specified methods are called on expressions

363

*/

364

@Target(ElementType.METHOD)

365

@Retention(RetentionPolicy.RUNTIME)

366

@InheritedAnnotation

367

public @interface EnsuresCalledMethods {

368

String[] value();

369

String[] methods();

370

}

371

372

/**

373

* Conditional method postcondition for called methods

374

*/

375

@Target(ElementType.METHOD)

376

@Retention(RetentionPolicy.RUNTIME)

377

@InheritedAnnotation

378

public @interface EnsuresCalledMethodsIf {

379

String[] expression();

380

String[] methods();

381

boolean result();

382

}

383

384

/**

385

* Exception postcondition: ensures methods are called when exception thrown

386

*/

387

@Target(ElementType.METHOD)

388

@Retention(RetentionPolicy.RUNTIME)

389

@InheritedAnnotation

390

public @interface EnsuresCalledMethodsOnException {

391

String[] value();

392

String[] methods();

393

Class<? extends Throwable>[] exception() default {Throwable.class};

394

}

395

396

/**

397

* Varargs version of EnsuresCalledMethods

398

*/

399

@Target(ElementType.METHOD)

400

@Retention(RetentionPolicy.RUNTIME)

401

@InheritedAnnotation

402

public @interface EnsuresCalledMethodsVarargs {

403

String[] value();

404

String[] methods();

405

}

406

407

/**

408

* Method precondition: requires specified methods to have been called

409

*/

410

@Target(ElementType.METHOD)

411

@Retention(RetentionPolicy.RUNTIME)

412

@InheritedAnnotation

413

public @interface RequiresCalledMethods {

414

String[] value();

415

String[] methods();

416

}

417

```

418

419

**Usage Examples:**

420

421

```java

422

import org.checkerframework.checker.calledmethods.qual.*;

423

424

public class ContractResourceExample {

425

private DatabaseConnection connection;

426

427

@EnsuresCalledMethods(value = "connection", methods = {"connect"})

428

public void initialize() {

429

connection = new DatabaseConnection();

430

connection.connect(); // Postcondition ensures connect() is called

431

}

432

433

@EnsuresCalledMethodsIf(expression = "connection", methods = {"connect", "authenticate"}, result = true)

434

public boolean isReady() {

435

return connection != null &&

436

connection.isConnected() &&

437

connection.isAuthenticated();

438

}

439

440

@RequiresCalledMethods(value = "connection", methods = {"connect"})

441

public void authenticate(String user, String password) {

442

// Precondition: connect() must have been called

443

connection.authenticate(user, password);

444

}

445

446

@EnsuresCalledMethodsOnException(value = "connection", methods = {"disconnect"}, exception = {SQLException.class})

447

public void riskyOperation() throws SQLException {

448

try {

449

connection.executeQuery("RISKY OPERATION");

450

} catch (SQLException e) {

451

connection.disconnect(); // Cleanup on exception

452

throw e;

453

}

454

}

455

456

public void safeUsage() throws SQLException {

457

initialize(); // Ensures connect() is called

458

authenticate("user", "pass"); // Safe - connect() was called

459

460

if (isReady()) {

461

// Safe - conditional postcondition ensures both methods called

462

connection.executeQuery("SELECT * FROM data");

463

}

464

465

try {

466

riskyOperation();

467

} catch (SQLException e) {

468

// Exception postcondition ensures disconnect() was called

469

System.out.println("Operation failed, connection cleaned up");

470

}

471

}

472

}

473

```