SRO.CLSJVM.LookingGlass 1.0.34
SRO.CLSJVM.LookingGlass - Looking Glass
Looking Glass - A view from one binary world to another.
LookingGlass is a research-grade .NET library from the Serengeti Research Organization (SRO) that provides full programmatic creation, reading, and manipulation of JVM (Java Virtual Machine) class files — the .class binary format — entirely from managed .NET code. No Java compiler or JDK toolchain is required to generate valid class files; the library implements the JVM Class File Specification directly.
The library targets both .NET Framework 4.8 and .NET 8.0, making it usable in both legacy Windows desktop applications and modern cross-platform environments.
Table of Contents
- Why Looking Glass?
- Solution Layout
- Library Architecture
- Project Structure
- Getting Started
- Key Concepts
- Building
- Test Projects
- NuGet Package
- Dependencies
- License
Why Looking Glass?
The Microsoft Roslyn compiler platform emits .NET IL binary files. LookingGlass is the research equivalent for the JVM side of that boundary — a toolkit powerful enough to serve as the back-end of a compiler that targets the JVM instead of the CLR. Because it works entirely in managed .NET code it can be embedded in any .NET application, service, or toolchain without requiring a JDK installation at runtime.
Concrete scenarios enabled by the library include:
- Cross-platform code generation — emit
.classfiles for any supported JDK version (1.1 through Java SE 28) from .NET code. - JVM bytecode tooling — read, disassemble, inspect, or transform existing
.classfiles inside a .NET process. - Compiler back-ends — use LookingGlass as the code-generation stage for a language targeting the JVM.
- Research and education — explore the JVM class file format, constant pool layout, access flags, and stack map frame verification without leaving the .NET ecosystem.
Solution Layout
SRO.CLSJVM.LookingGlass/
+-- SRO.CLSJVM.LookingGlass.sln # Solution file
+-- README.md # This file
|
+-- SRO.CLSJVM.LookingGlass/ # Main library project (net48 + net8.0)
| +-- SRO.CLSJVM.LookingGlass.csproj
| +-- Foundation/ # Foundation namespace -- binary I/O primitives
| +-- ByteCodeEngine/ # Translation engine -- Model <-> binary
| +-- [ModelAPI source files] # Root namespace -- high-level object model
|
+-- LookingGlassTests/ # WinForms visual test harness (net48)
| +-- LookingGlassTests.csproj
|
+-- UnitTests/ # xUnit automated test suite (net8.0)
+-- UnitTests.csproj
Library Architecture
LookingGlass ships as a single NuGet package (SRO.CLSJVM.LookingGlass) containing three conceptual layers organized by namespace. The Foundation layer uses the sub-namespace SRO.CLSJVM.LookingGlass.Foundation; the Model and Translation layers share the root SRO.CLSJVM.LookingGlass namespace.
+------------------------------------------+
| Your Application / Compiler |
+-------------------+----------------------+
|
+-----------v---------------------------------+
| SRO.CLSJVM.LookingGlass |
| (single library / package) |
| |
| +--------------------------------------+ |
| | Model Layer (root namespace) | |
| | ClassModel, MethodModel, CodeBlock | |
| +----------+------------------+--------+ |
| | | |
| +----------+------+ +--------+---------+ |
| | Foundation | | Translation | |
| | (.Foundation | | Engine | |
| | namespace) | | ClassBuilder | |
| | ClassEditor | | ClassDeconstructor| |
| | MethodEditor | +------------------+ |
| +-----------------+ |
+---------------------------------------------+
Foundation Layer
Namespace: SRO.CLSJVM.LookingGlass.Foundation
The Foundation layer implements every detail of the JVM Class File Specification at the byte level. It is the lowest layer and is what ultimately reads and writes the 0xCAFEBABE magic-number binary format.
| Type | Purpose |
|---|---|
ClassEditor |
Core editing engine — creates, reads, and writes JVM class files. Manages the constant pool, fields, methods, interfaces, and class-level attributes. |
MethodEditor |
Manages a single method's bytecode, local variable table, exception table, and stack map attribute. |
FieldEditor |
Manages a single field definition including its descriptor, access flags, and constant value. |
BCEUtils |
Static utilities for translating between JVM field descriptor characters (B, C, D, …), Java primitive type names, and newarray type codes. |
BCEClass |
Models the Java Class concept — encodes and decodes JVM field descriptors for primitives, reference types, and arrays of any dimension. |
PoolEntry (and subclasses) |
Complete hierarchy of constant pool entry types: CONSTANT_Class, CONSTANT_Fieldref, CONSTANT_Methodref, CONSTANT_InterfaceMethodref, CONSTANT_String, CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long, CONSTANT_Double, CONSTANT_NameAndType, CONSTANT_Utf8, CONSTANT_MethodHandle, CONSTANT_MethodType, CONSTANT_InvokeDynamic. |
Opcode (and subclasses) |
Strongly typed wrapper classes for every JVM opcode (AALOAD through XRETURN). |
AttributeInfo (and subclasses) |
Full attribute hierarchy: CodeAttributeInfo, StackMapTableAttribute, ExceptionsAttribute, LineNumberTableAttribute, LocalVariableTableAttribute, SignatureAttribute, SourceFileAttribute, BootstrapMethodsAttribute, InnerClassesAttribute, and more. |
StackMapFrameProcesser |
Performs code-flow analysis on a method's opcode stream to compute and emit a valid StackMapTable attribute, required by the JVM verifier for class files targeting JDK 6 and later. |
ModifierConstants |
[Flags] enum of JVM access flag bits: PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, SYNCHRONIZED/SUPER, VOLATILE/BRIDGE, TRANSIENT/VARARG, NATIVE, INTERFACE, ABSTRACT, STRICT. |
AutoAllocationDirective |
[Flags] enum that controls which aspects of a method (MaxLocals, MaxStack, stack map frames) are calculated automatically. |
LookingGlassFoundationException |
Base exception type for Foundation-layer errors. |
Model Layer
Namespace: SRO.CLSJVM.LookingGlass
The Model layer provides a clean, high-level object model that insulates callers from the binary-level details of the Foundation layer. It is the primary surface for application code and compiler back-ends.
| Type | Purpose |
|---|---|
ClassModel |
Top-level representation of a Java class: name, superclass, interfaces, access flags, JVM version emulation string, fields, methods, inner classes. |
FieldModel |
Represents one field: name, type descriptor, access flags, optional constant value. |
MethodModel |
Represents one method: name, parameter/return descriptors, access flags, code block. |
CodeBlock |
Container for an ordered list of Instruction objects and ExceptionHandlerModel entries that form a method body. |
Instruction |
A single JVM assembly instruction: opcode name, optional label, typed arguments (IEmissionArgument). |
OpcodeValues |
Enum of every JVM opcode as a named constant (NOP, ALOAD_0, INVOKESPECIAL, NEW, …). |
OpcodeMap / OpcodeTableInfoRow |
Static lookup table recording each opcode's operand-stack delta and flow-control classification. |
BaseDescriptor / MethodDescriptor |
Parse and model JVM field and method descriptor strings. |
SignatureParsing |
Tokeniser and parser for Java generic signature strings. |
InstructionSpecDefinition |
Per-opcode specification of operand-stack push/pop/replace effects, used during stack-state simulation. |
IBCEngineIOContract |
Interface decoupling the Model layer from any specific binary serialisation implementation; defines SaveBinary, LoadBinary, SaveStream, and LoadStream. |
ModelFileIO |
Dispatcher that discovers and delegates to a concrete IBCEngineIOContract implementation at runtime, plus direct XML serialisation helpers. |
GlassException |
Common exception type for Model-layer errors. |
Constants |
Well-known JVM string constants: <init>, <clinit>. |
Translation Engine
Namespace: SRO.CLSJVM.LookingGlass
The Translation Engine bridges the Model and Foundation layers by implementing the translation in both directions.
| Type | Purpose |
|---|---|
ClassBuilder |
Translates a ClassModel object graph into a binary JVM class file. Drives a ClassEditor from the Foundation layer. Supports debug mode, JVM version emulation override, and automatic stack map/local/stack-size allocation. |
ClassDeconstructor |
Parses binary JVM class bytes (via a ClassEditor) into a ClassModel object graph, enabling round-trip read-modify-write workflows. |
ModelFileIOImpl |
Concrete implementation of IBCEngineIOContract — plugged in automatically by ModelFileIO at runtime. |
Project Structure
| Project | Namespace(s) | Target Frameworks | Role |
|---|---|---|---|
SRO.CLSJVM.LookingGlass |
SRO.CLSJVM.LookingGlass, SRO.CLSJVM.LookingGlass.Foundation |
net48, net8.0 |
Main library — model, foundation, and translation engine |
LookingGlassTests |
LookingGlassTests |
net48 |
WinForms visual test harness (manual) |
UnitTests |
UnitTests |
net8.0 |
xUnit automated test suite |
Getting Started
Creating a Class from Scratch
The typical workflow using the Model and Translation layers:
using SRO.CLSJVM.LookingGlass;
// 1. Describe the class
ClassModel cm = new ClassModel();
cm.ClassName = "com/example/Hello";
cm.SuperClassName = "java/lang/Object";
cm.Public = true;
cm.Emulation = "SE11"; // target Java 11 class-file format
// 2. Add a default constructor
MethodModel ctor = new MethodModel();
ctor.Name = Constants.CONSTRUCTOR_NAME; // "<init>"
ctor.Public = true;
ctor.ReturnsTypeDescriptor = "V";
CodeBlock ctorBody = new CodeBlock();
ctorBody.AddInstruction(new Instruction(OpcodeValues.ALOAD_0));
Instruction superCall = new Instruction(OpcodeValues.INVOKESPECIAL);
superCall.AddMethodSpecArgument("java/lang/Object", "<init>", "()V");
ctorBody.AddInstruction(superCall);
ctorBody.AddInstruction(new Instruction(OpcodeValues.RETURN));
ctor.Block = ctorBody;
ctor.AutoAllocate();
cm.AddMethod(ctor);
// 3. Add a main method that prints "Hello, World!"
MethodModel main = new MethodModel();
main.Name = "main";
main.Public = true;
main.Static = true;
main.ReturnsTypeDescriptor = "V";
main.AddParameterDescriptor("[Ljava/lang/String;");
CodeBlock mainBody = new CodeBlock();
Instruction getStatic = new Instruction(OpcodeValues.GETSTATIC);
getStatic.AddFieldSpecArgument("java/lang/System", "out", "Ljava/io/PrintStream;");
mainBody.AddInstruction(getStatic);
Instruction ldc = new Instruction(OpcodeValues.LDC);
ldc.AddStringArgument("Hello, World!");
mainBody.AddInstruction(ldc);
Instruction println = new Instruction(OpcodeValues.INVOKEVIRTUAL);
println.AddMethodSpecArgument("java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mainBody.AddInstruction(println);
mainBody.AddInstruction(new Instruction(OpcodeValues.RETURN));
main.Block = mainBody;
main.AutoAllocate();
cm.AddMethod(main);
// 4. Emit the class file
ModelFileIO.SaveBinary("Hello.class", cm);
Reading an Existing Class File
using SRO.CLSJVM.LookingGlass;
// Load a .class file back into an object model
ClassModel cm = ModelFileIO.LoadBinary("Hello.class");
Console.WriteLine("Class : " + cm.ClassName);
Console.WriteLine("Super : " + cm.SuperClassName);
Console.WriteLine("JVM ver : " + cm.Emulation);
foreach (MethodModel m in cm.Methods)
{
Console.WriteLine(" Method: " + m.SourceView);
}
Key Concepts
ClassModel Hierarchy
ClassModel
+-- List<FieldModel> - fields with descriptors and access flags
+-- List<MethodModel> - methods
| +-- CodeBlock - instruction stream + exception handlers
| +-- List<Instruction>
| +-- List<ExceptionHandlerModel>
+-- List<InnerClassModel> - inner/nested class declarations
+-- List<string> - implemented interfaces
JVM Version Emulation
The ClassModel.Emulation property selects the target JVM major/minor version written into the class file header. Supported values span the full range from "1.1" through "SE28". The Translation Engine automatically adjusts whether a StackMapTable attribute is required (mandatory for class files with major_version >= 50, i.e., JDK 6+).
cm.Emulation = "SE17"; // Java SE 17 -- major_version = 61
Automatic Allocation
Callers do not need to manually compute maximum stack depth, local variable slot counts, or stack map frame entries. Calling AutoAllocate() on a MethodModel (or ClassModel) triggers automatic analysis:
mm.AutoAllocate(); // computes MaxLocals, MaxStack, and StackMapTable entries
The AutoAllocationDirective flags allow fine-grained control when manual values are preferred:
mm.AutomaticStackMapGeneration = true; // let LookingGlass generate the StackMapTable
mm.AutomaticStackSize = true; // compute max_stack automatically
mm.AutomaticLocals = true; // compute max_locals automatically
Stack Map Frame Generation
Class files targeting Java SE 6 or later require a StackMapTable attribute on any method that contains control-flow branches (conditionals, loops, exception handlers). LookingGlass generates this attribute automatically via StackMapFrameProcesser (Foundation layer), which performs a code-flow simulation over the opcode stream, tracking operand stack and local variable types at each branch target.
IBCEngineIOContract - Pluggable I/O
The IBCEngineIOContract interface separates the high-level model from the binary serialisation implementation:
public interface IBCEngineIOContract
{
void SaveBinary(string path, ClassModel model);
ClassModel LoadBinary(string path);
void SaveStream(Stream stream, ClassModel model);
ClassModel LoadStream(Stream stream);
}
ModelFileIO discovers a concrete implementation by scanning the application's assembly directory at startup, so alternative or extended engines can be substituted by dropping a replacement assembly alongside the host application — no recompilation required.
Building
The library is an SDK-style, multi-target project. Build from within the solution directory:
# Restore and build all projects
dotnet build SRO.CLSJVM.LookingGlass.sln
# Build only the main library
dotnet build SRO.CLSJVM.LookingGlass\SRO.CLSJVM.LookingGlass.csproj
# Produce a NuGet package (GeneratePackageOnBuild is enabled by default)
dotnet pack SRO.CLSJVM.LookingGlass\SRO.CLSJVM.LookingGlass.csproj -c Release
NuGet packages are generated automatically on each build (GeneratePackageOnBuild = true) and land in the project's bin\Debug or bin\Release output folder.
Test Projects
UnitTests (xUnit - .NET 8.0)
The automated test suite in UnitTests\UnitTestsFramework.cs drives the full round-trip workflow: generate a ClassModel, emit a .class file, invoke the host JRE to execute the class, and assert the standard output matches expectations.
Tests are organized by scenario:
| Test Group | Coverage |
|---|---|
| StackFrame Handling — JDK 1.1 through SE25 | 25 tests verifying correct class-file generation across the full supported JDK version range — from pre-StackMapTable levels through the latest emulation targets. |
| Gap Tests — Arithmetic / Long Compare / Null Branch (Gaps 1a, 1b, 1c) | Code-flow simulation of IADD, LCMP, and IFNULL opcodes preceding branch targets. |
| Gap Tests — LDC and Wide Stores (Gaps 1d, 1f) | LDC/LDC_W constant loading; LSTORE, FSTORE, DSTORE wide-slot stores. |
| Gap Tests — ASTORE Locals / NEW Opcode / Finally Handler (Gaps 2, 3, 5) | ASTORE/ASTORE_N local-variable writes; NEW/DUP uninitialized-reference tracking; catch-all (finally) handler frame injection. |
| Gap Tests — Long Parameter Alignment / Type Merge (Gaps 6, 7) | Two-slot long/double parameter seeding in initial locals; fixed-point iteration at control-flow join points. |
| Gap Tests — TABLESWITCH (Gap 8a) | Switch statements compiled to the TABLESWITCH opcode. |
| Gap Tests — LOOKUPSWITCH (Gap 8b) | Switch statements compiled to the LOOKUPSWITCH opcode. |
| Gap Tests — Exception Handler Locals (Gap 9) | Local variable state at exception handler entry points. |
| Version Map Property Tests | Verifies ClassModel.Emulation string-to-major-version mappings for all supported JDK levels. |
| Preview Feature Tests | Class files using Java preview language features (--enable-preview). |
| JSR/RET Validation Tests | JSR/RET subroutine opcodes as found in older class files. |
| Class File Reading — Basic Round-Trip | High-level API: emit a class file and reload it via ModelFileIO.LoadBinary. |
| Class File Reading — Modifiers and Structure (Tests 1–24) | 24 tests covering class modifiers (public, abstract, final, interface, annotation, enum, record, sealed), field modifiers and constant values, method modifiers (abstract, synchronized, native, vararg, bridge), superclass name, interface lists, inner and anonymous classes, package detection, SourceFile attribute, and text block string constants. |
Note: A JRE installation accessible on the system
PATHis required for the xUnit tests that execute generated class files.
LookingGlassTests (WinForms - .NET Framework 4.8)
A Windows Forms GUI harness for interactive and visual testing. It allows a developer to load any .class file, inspect the resulting ClassModel (fields, methods, constant pool, XML serialisation view), edit class-level properties, and save a modified class back to disk. Run this project directly from Visual Studio.
NuGet Package
| Package ID | Version | Description |
|---|---|---|
SRO.CLSJVM.LookingGlass |
1.0.0 | JVM class file creation, reading, and manipulation library |
This README.md is embedded as the NuGet package README file.
Dependencies
| Dependency | Version | Used By |
|---|---|---|
SRO.IO.DataBag |
1.0.0 | Main library — primitive I/O for binary stream read/write |
xunit |
2.9.3 | UnitTests |
xunit.runner.visualstudio |
3.1.5 | UnitTests |
Microsoft.NET.Test.Sdk |
18.7.0 | UnitTests |
System.Reflection.Metadata |
8.0.0 | UnitTests |
coverlet.collector |
10.0.1 | UnitTests — code coverage collection |
License
Copyright (c) 2011-2026 Serengeti Research Organization. All rights reserved.
Showing the top 20 packages that depend on SRO.CLSJVM.LookingGlass.
| Packages | Downloads |
|---|---|
|
SRO.CLSJVM.RefResolution
This multi-platform API provides provides facilities for looking up code entities (reference resolution) such as classes, methods, fields etc for the JVM platform. It is specifically designed to find these entities from compiled data sources such as JARs and directory structures and in the future JIMAGE modules file, as well as gathered source information from early stages of source code building.
|
7 |
|
SRO.CLSJVM.RefResolution
This multi-platform API provides provides facilities for looking up code entities (reference resolution) such as classes, methods, fields etc for the JVM platform. It is specifically designed to find these entities from compiled data sources such as JARs and directory structures and in the future JIMAGE modules file, as well as gathered source information from early stages of source code building.
|
6 |
|
SRO.CLSJVM.RefResolution
This multi-platform API provides provides facilities for looking up code entities (reference resolution) such as classes, methods, fields etc for the JVM platform. It is specifically designed to find these entities from compiled data sources such as JARs and directory structures and in the future JIMAGE modules file, as well as gathered source information from early stages of source code building.
|
5 |
|
SRO.CLSJVM.RefResolution
This multi-platform API provides provides facilities for looking up code entities (reference resolution) such as classes, methods, fields etc for the JVM platform. It is specifically designed to find these entities from compiled data sources such as JARs and directory structures and in the future JIMAGE modules file, as well as gathered source information from early stages of source code building.
|
4 |
.NET Framework 4.7.2
- SRO.IO.DataBag (>= 1.0.1)
.NET Framework 4.8
- SRO.IO.DataBag (>= 1.0.1)
.NET 8.0
- SRO.IO.DataBag (>= 1.0.1)
.NET 9.0
- SRO.IO.DataBag (>= 1.0.1)
.NET Standard 2.0
- SRO.IO.DataBag (>= 1.0.1)
.NET Standard 2.1
- SRO.IO.DataBag (>= 1.0.1)
| Version | Downloads | Last updated |
|---|---|---|
| 1.0.46 | 6 | 10/25/2026 |
| 1.0.45 | 2 | 10/24/2026 |
| 1.0.44 | 4 | 10/17/2026 |
| 1.0.43 | 3 | 10/16/2026 |
| 1.0.42 | 7 | 10/16/2026 |
| 1.0.41 | 2 | 10/16/2026 |
| 1.0.40 | 3 | 10/15/2026 |
| 1.0.39 | 4 | 10/15/2026 |
| 1.0.38 | 3 | 10/14/2026 |
| 1.0.37 | 2 | 10/13/2026 |
| 1.0.36 | 3 | 10/09/2026 |
| 1.0.35 | 4 | 10/08/2026 |
| 1.0.34 | 2 | 10/08/2026 |
| 1.0.33 | 3 | 10/07/2026 |
| 1.0.31 | 3 | 10/06/2026 |
| 1.0.30 | 1 | 10/06/2026 |
| 1.0.29 | 3 | 10/03/2026 |
| 1.0.28 | 3 | 10/02/2026 |
| 1.0.27 | 3 | 10/01/2026 |
| 1.0.26 | 3 | 10/01/2026 |
| 1.0.25 | 2 | 09/30/2026 |
| 1.0.24 | 4 | 09/28/2026 |
| 1.0.23 | 4 | 09/27/2026 |
| 1.0.22 | 5 | 09/25/2026 |
| 1.0.21 | 3 | 09/24/2026 |
| 1.0.20 | 5 | 09/23/2026 |
| 1.0.19 | 4 | 09/23/2026 |
| 1.0.18 | 4 | 09/23/2026 |
| 1.0.17 | 6 | 09/02/2026 |