or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-management.mdengine-management.mdexecution-monitoring.mdindex.mdio-abstractions.mdproxy-system.mdsecurity-configuration.mdsource-management.mdvalue-operations.md

proxy-system.mddocs/

0

# Proxy System

1

2

The proxy system enables Java objects to be seamlessly exposed to guest languages with customizable behaviors. Proxy interfaces allow Java objects to participate in polyglot interactions by implementing language-specific protocols for object access, function execution, and data manipulation.

3

4

## Capabilities

5

6

### Base Proxy Interface

7

8

All proxy types implement the base Proxy interface.

9

10

```java { .api }

11

public interface Proxy {

12

// Marker interface - no methods

13

}

14

```

15

16

### Object Proxies

17

18

Enable Java objects to behave like guest language objects with named members.

19

20

```java { .api }

21

public interface ProxyObject extends Proxy {

22

Object getMember(String key);

23

Object[] getMemberKeys();

24

boolean hasMember(String key);

25

void putMember(String key, Value value);

26

boolean removeMember(String key);

27

}

28

```

29

30

**Usage:**

31

32

```java

33

public class PersonProxy implements ProxyObject {

34

private final Map<String, Object> properties = new HashMap<>();

35

36

public PersonProxy(String name, int age) {

37

properties.put("name", name);

38

properties.put("age", age);

39

}

40

41

@Override

42

public Object getMember(String key) {

43

return properties.get(key);

44

}

45

46

@Override

47

public Object[] getMemberKeys() {

48

return properties.keySet().toArray();

49

}

50

51

@Override

52

public boolean hasMember(String key) {

53

return properties.containsKey(key);

54

}

55

56

@Override

57

public void putMember(String key, Value value) {

58

properties.put(key, value.isString() ? value.asString() : value);

59

}

60

61

@Override

62

public boolean removeMember(String key) {

63

return properties.remove(key) != null;

64

}

65

}

66

67

// Usage in context

68

try (Context context = Context.create("js")) {

69

PersonProxy person = new PersonProxy("Alice", 30);

70

context.getBindings("js").putMember("person", person);

71

72

// JavaScript can access as object

73

context.eval("js", "console.log(person.name)"); // "Alice"

74

context.eval("js", "person.age = 31"); // Modify age

75

context.eval("js", "person.city = 'New York'"); // Add new property

76

context.eval("js", "console.log(person.city)"); // "New York"

77

}

78

```

79

80

### Array Proxies

81

82

Enable Java objects to behave like guest language arrays.

83

84

```java { .api }

85

public interface ProxyArray extends ProxyIterable {

86

Object get(long index);

87

void set(long index, Value value);

88

long getSize();

89

boolean remove(long index);

90

}

91

```

92

93

**Usage:**

94

95

```java

96

public class ListProxy implements ProxyArray {

97

private final List<Object> list;

98

99

public ListProxy(List<Object> list) {

100

this.list = list;

101

}

102

103

@Override

104

public Object get(long index) {

105

return list.get((int) index);

106

}

107

108

@Override

109

public void set(long index, Value value) {

110

list.set((int) index, value.isString() ? value.asString() : value);

111

}

112

113

@Override

114

public long getSize() {

115

return list.size();

116

}

117

118

@Override

119

public boolean remove(long index) {

120

try {

121

list.remove((int) index);

122

return true;

123

} catch (IndexOutOfBoundsException e) {

124

return false;

125

}

126

}

127

128

@Override

129

public Object getIterator() {

130

return new IteratorProxy(list.iterator());

131

}

132

}

133

134

// Usage

135

try (Context context = Context.create("js")) {

136

ListProxy array = new ListProxy(new ArrayList<>(Arrays.asList("a", "b", "c")));

137

context.getBindings("js").putMember("array", array);

138

139

// JavaScript can access as array

140

context.eval("js", "console.log(array[0])"); // "a"

141

context.eval("js", "array[1] = 'modified'"); // Modify element

142

context.eval("js", "console.log(array.length)"); // Array size

143

}

144

```

145

146

### Executable Proxies

147

148

Enable Java objects to be called as functions.

149

150

```java { .api }

151

public interface ProxyExecutable extends Proxy {

152

Object execute(Value... arguments);

153

}

154

```

155

156

**Usage:**

157

158

```java

159

public class MathFunction implements ProxyExecutable {

160

private final String operation;

161

162

public MathFunction(String operation) {

163

this.operation = operation;

164

}

165

166

@Override

167

public Object execute(Value... arguments) {

168

if (arguments.length != 2) {

169

throw new IllegalArgumentException("Expected 2 arguments");

170

}

171

172

double a = arguments[0].asDouble();

173

double b = arguments[1].asDouble();

174

175

switch (operation) {

176

case "add": return a + b;

177

case "multiply": return a * b;

178

case "divide": return b != 0 ? a / b : Double.POSITIVE_INFINITY;

179

default: throw new IllegalArgumentException("Unknown operation: " + operation);

180

}

181

}

182

}

183

184

// Usage

185

try (Context context = Context.create("js")) {

186

MathFunction addFunc = new MathFunction("add");

187

context.getBindings("js").putMember("add", addFunc);

188

189

// JavaScript can call as function

190

Value result = context.eval("js", "add(10, 20)");

191

System.out.println("Result: " + result.asDouble()); // 30.0

192

}

193

```

194

195

### Instantiable Proxies

196

197

Enable Java objects to be used as constructors.

198

199

```java { .api }

200

public interface ProxyInstantiable extends Proxy {

201

Object newInstance(Value... arguments);

202

}

203

```

204

205

**Usage:**

206

207

```java

208

public class PersonConstructor implements ProxyInstantiable {

209

@Override

210

public Object newInstance(Value... arguments) {

211

if (arguments.length < 2) {

212

throw new IllegalArgumentException("Person requires name and age");

213

}

214

215

String name = arguments[0].asString();

216

int age = arguments[1].asInt();

217

218

return new PersonProxy(name, age);

219

}

220

}

221

222

// Usage

223

try (Context context = Context.create("js")) {

224

PersonConstructor PersonClass = new PersonConstructor();

225

context.getBindings("js").putMember("Person", PersonClass);

226

227

// JavaScript can use as constructor

228

context.eval("js", "const alice = new Person('Alice', 30)");

229

context.eval("js", "console.log(alice.name)"); // "Alice"

230

}

231

```

232

233

### Iterable and Iterator Proxies

234

235

Enable Java objects to support iteration protocols.

236

237

```java { .api }

238

public interface ProxyIterable extends Proxy {

239

Object getIterator();

240

}

241

242

public interface ProxyIterator extends Proxy {

243

boolean hasNext();

244

Object getNext();

245

}

246

```

247

248

**Usage:**

249

250

```java

251

public class IteratorProxy implements ProxyIterator {

252

private final Iterator<?> iterator;

253

254

public IteratorProxy(Iterator<?> iterator) {

255

this.iterator = iterator;

256

}

257

258

@Override

259

public boolean hasNext() {

260

return iterator.hasNext();

261

}

262

263

@Override

264

public Object getNext() {

265

return iterator.next();

266

}

267

}

268

269

public class RangeProxy implements ProxyIterable {

270

private final int start, end;

271

272

public RangeProxy(int start, int end) {

273

this.start = start;

274

this.end = end;

275

}

276

277

@Override

278

public Object getIterator() {

279

return new IteratorProxy(IntStream.range(start, end).iterator());

280

}

281

}

282

283

// Usage

284

try (Context context = Context.create("js")) {

285

RangeProxy range = new RangeProxy(1, 5);

286

context.getBindings("js").putMember("range", range);

287

288

// JavaScript can iterate

289

context.eval("js", "for (const num of range) { console.log(num); }"); // 1, 2, 3, 4

290

}

291

```

292

293

### Hash Map Proxies

294

295

Enable Java objects to behave like guest language maps/dictionaries.

296

297

```java { .api }

298

public interface ProxyHashMap extends Proxy {

299

long getHashSize();

300

boolean hasHashEntry(Value key);

301

Value getHashValue(Value key);

302

void putHashEntry(Value key, Value value);

303

boolean removeHashEntry(Value key);

304

Value getHashEntriesIterator();

305

Value getHashKeysIterator();

306

Value getHashValuesIterator();

307

}

308

```

309

310

**Usage:**

311

312

```java

313

public class MapProxy implements ProxyHashMap {

314

private final Map<Object, Object> map = new HashMap<>();

315

316

@Override

317

public long getHashSize() {

318

return map.size();

319

}

320

321

@Override

322

public boolean hasHashEntry(Value key) {

323

return map.containsKey(convertKey(key));

324

}

325

326

@Override

327

public Value getHashValue(Value key) {

328

Object value = map.get(convertKey(key));

329

return value != null ? Value.asValue(value) : null;

330

}

331

332

@Override

333

public void putHashEntry(Value key, Value value) {

334

map.put(convertKey(key), convertValue(value));

335

}

336

337

@Override

338

public boolean removeHashEntry(Value key) {

339

return map.remove(convertKey(key)) != null;

340

}

341

342

// Helper methods

343

private Object convertKey(Value key) {

344

if (key.isString()) return key.asString();

345

if (key.isNumber()) return key.asInt();

346

return key;

347

}

348

349

private Object convertValue(Value value) {

350

if (value.isString()) return value.asString();

351

if (value.isNumber()) return value.asDouble();

352

if (value.isBoolean()) return value.asBoolean();

353

return value;

354

}

355

356

// Iterator implementations omitted for brevity

357

@Override

358

public Value getHashEntriesIterator() { /* ... */ }

359

@Override

360

public Value getHashKeysIterator() { /* ... */ }

361

@Override

362

public Value getHashValuesIterator() { /* ... */ }

363

}

364

```

365

366

### Date/Time Proxies

367

368

Enable Java objects to represent date and time values in guest languages.

369

370

```java { .api }

371

public interface ProxyDate extends Proxy {

372

LocalDate asDate();

373

}

374

375

public interface ProxyTime extends Proxy {

376

LocalTime asTime();

377

}

378

379

public interface ProxyTimeZone extends Proxy {

380

ZoneId asTimeZone();

381

}

382

383

public interface ProxyDuration extends Proxy {

384

Duration asDuration();

385

}

386

387

public interface ProxyInstant extends ProxyDate, ProxyTime, ProxyTimeZone {

388

Instant asInstant();

389

}

390

```

391

392

**Usage:**

393

394

```java

395

public class DateTimeProxy implements ProxyInstant {

396

private final ZonedDateTime dateTime;

397

398

public DateTimeProxy(ZonedDateTime dateTime) {

399

this.dateTime = dateTime;

400

}

401

402

@Override

403

public LocalDate asDate() {

404

return dateTime.toLocalDate();

405

}

406

407

@Override

408

public LocalTime asTime() {

409

return dateTime.toLocalTime();

410

}

411

412

@Override

413

public ZoneId asTimeZone() {

414

return dateTime.getZone();

415

}

416

417

@Override

418

public Instant asInstant() {

419

return dateTime.toInstant();

420

}

421

}

422

423

// Usage

424

try (Context context = Context.create("js")) {

425

DateTimeProxy now = new DateTimeProxy(ZonedDateTime.now());

426

context.getBindings("js").putMember("now", now);

427

428

// JavaScript can work with as date

429

context.eval("js", "console.log(new Date(now))");

430

}

431

```

432

433

## Advanced Proxy Patterns

434

435

### Composite Proxies

436

437

Implement multiple proxy interfaces for rich behavior:

438

439

```java

440

public class SmartArray implements ProxyArray, ProxyObject, ProxyExecutable {

441

private final List<Object> data = new ArrayList<>();

442

443

// ProxyArray implementation

444

@Override

445

public Object get(long index) { return data.get((int) index); }

446

@Override

447

public void set(long index, Value value) { /* ... */ }

448

@Override

449

public long getSize() { return data.size(); }

450

@Override

451

public boolean remove(long index) { /* ... */ }

452

@Override

453

public Object getIterator() { /* ... */ }

454

455

// ProxyObject implementation (for methods like push, pop, etc.)

456

@Override

457

public Object getMember(String key) {

458

switch (key) {

459

case "push": return new PushFunction();

460

case "pop": return new PopFunction();

461

case "length": return data.size();

462

default: return null;

463

}

464

}

465

// ... other ProxyObject methods

466

467

// ProxyExecutable implementation (for functional behavior)

468

@Override

469

public Object execute(Value... arguments) {

470

// Array can be called as function (e.g., for filtering)

471

if (arguments.length == 1 && arguments[0].canExecute()) {

472

return data.stream()

473

.filter(item -> arguments[0].execute(item).asBoolean())

474

.collect(Collectors.toList());

475

}

476

throw new UnsupportedOperationException();

477

}

478

}

479

```

480

481

### Dynamic Proxies

482

483

Create proxies that adapt behavior based on usage:

484

485

```java

486

public class DynamicProxy implements ProxyObject, ProxyExecutable {

487

private final Map<String, Object> properties = new HashMap<>();

488

private final Object target;

489

490

public DynamicProxy(Object target) {

491

this.target = target;

492

// Introspect target and populate properties

493

populateFromTarget();

494

}

495

496

@Override

497

public Object getMember(String key) {

498

// First check explicit properties

499

if (properties.containsKey(key)) {

500

return properties.get(key);

501

}

502

503

// Then try reflection on target

504

try {

505

Method method = target.getClass().getMethod(key);

506

return new MethodProxy(target, method);

507

} catch (NoSuchMethodException e) {

508

return null;

509

}

510

}

511

512

// ... implement other methods

513

}

514

515

class MethodProxy implements ProxyExecutable {

516

private final Object target;

517

private final Method method;

518

519

public MethodProxy(Object target, Method method) {

520

this.target = target;

521

this.method = method;

522

}

523

524

@Override

525

public Object execute(Value... arguments) {

526

try {

527

Object[] args = Arrays.stream(arguments)

528

.map(this::convertArgument)

529

.toArray();

530

return method.invoke(target, args);

531

} catch (Exception e) {

532

throw new RuntimeException(e);

533

}

534

}

535

536

private Object convertArgument(Value arg) {

537

// Convert Value to appropriate Java type

538

// ... implementation

539

}

540

}

541

```

542

543

## Performance Considerations

544

545

- **Interface Segregation**: Only implement proxy interfaces you actually need

546

- **Lazy Evaluation**: Defer expensive operations until actually called

547

- **Caching**: Cache computed values and method lookups when possible

548

- **Type Conversion**: Minimize unnecessary type conversions between Value and Java objects

549

- **Memory Management**: Be careful with object references in long-running contexts

550

551

## Error Handling

552

553

Proxy methods should handle errors gracefully:

554

555

```java

556

public class SafeProxy implements ProxyObject {

557

@Override

558

public Object getMember(String key) {

559

try {

560

return doGetMember(key);

561

} catch (Exception e) {

562

// Log error, return null, or throw PolyglotException

563

throw new PolyglotException("Error accessing member: " + key, e);

564

}

565

}

566

567

// ... other methods with similar error handling

568

}

569

```