Public
Edited
Sep 23, 2024
Insert cell
Insert cell
viewof filesToUpload = Inputs.file({
title: "a class file",
description: "class file",
accept: "*",
multiple: false
})
Insert cell
Insert cell
hello = FileAttachment("Hello.class")
Insert cell
Insert cell
classFile(0, cafebabe)
Insert cell
classFile(0, cafebabe)?.unwrap().value.value.value.methods.value[0].value.value
.attributes.value[0].value.value.info.value
Insert cell
new JAttribute(
classFile(
0,
cafebabe
)?.unwrap().value.value.value.methods.value[0].value.value.attributes.value[0],
classFile(0, cafebabe)?.value.value.value.value.constant_pool
)
Insert cell
function getJObjects(result) {
const methods = result.value.value.value.methods.value.map((method) => {
return new JMethod(method, result.value.value.value.constant_pool);
});

// Extract all attributes from all methods
const attributes = methods.flatMap((method) => method.attributes);

// Extract all CodeAttributes from attributes
const codeAttributes = attributes
.filter((attr) => attr.infoParsed instanceof JCodeAttribute)
.map((attr) => attr.infoParsed);

return { methods, attributes, codeAttributes };
}
Insert cell
getJObjects(classFile(0, cafebabe).unwrap())
Insert cell
classFile(0, cafebabe)?.value.value.value.value.methods.value[0]
Insert cell
new JMethod(
classFile(0, cafebabe)?.value.value.value.value.methods.value[0],
classFile(0, cafebabe)?.value.value.value.value.constant_pool
).attributes[0].infoParsed.code
Insert cell
new JMethod(
classFile(0, cafebabe)?.value.value.value.value.methods.value[0],
classFile(0, cafebabe)?.value.value.value.value.constant_pool
)
Insert cell
JMethod = J.JMethod
Insert cell
JAttribute = J.JAttribute
Insert cell
JCodeAttribute = J.JCodeAttribute
Insert cell
J = {
//dings is the parsed element, pool is a reference to the constant_pool of the class
function JMethod(dings, pool) {
this.name = pool.value[dings.value.value.name_index.value].value.value;
this.descriptor =
pool.value[dings.value.value.descriptor_index.value].value.value;
this.access = dings.value.value.access_flags.value;
this.original = dings;
this.attributes = dings.value.value.attributes.value.map((_) => {
return new JAttribute(_, pool);
});
}
function JAttribute(dings, pool) {
this.name =
pool.value[dings.value.value.attribute_name_index.value].value.value;
this.info = dings.value.value.info.value;
console.log(this.name);
if (this.name == "Code") {
this.infoParsed = this.infoParsed = new JCodeAttribute(
parseCodeAttribute(0, this.info).unwrap().value,
pool
);
}
this.original = dings;
}

function JCodeAttribute(dings, pool) {
this.max_stack = dings.value.value.max_stack.value;
this.max_locals = dings.value.value.max_locals.value;
this.exception_table = dings.value.value.exception_table.value;
this.code = dings.value.value.code.value;
this.attributes = dings.value.value.attributes.value.map((_) => {
return new JAttribute(_, pool);
});
this.original = dings;
}

return { JAttribute, JCodeAttribute, JMethod };
}
Insert cell
classFile(0, cafebabe).unwrap().value.value.value.constant_pool
Insert cell
import {
classFile,
createConstantInfoParser,
u2be,
applyNTimes,
flatMapParser,
u4be,
u1,
takeN,
parseAttributeInfo,
expectP,
orParser,
mapParser,
Result,
chainParser,
chainMapParser,
ByteSlice,
ParseEnum,
acceptAndDoNothing
} from "@kreijstal/java-class-parser"
Insert cell
parseCodeAttribute = createConstantInfoParser("CodeAttribute", [
[u2be, "max_stack"],
[u2be, "max_locals"],
[
flatMapParser(u4be, (lengthValueEnum) =>
takeN(lengthValueEnum.value.value)
),
"code"
],
[
flatMapParser(u2be, (lengthValueEnum) =>
applyNTimes(
createConstantInfoParser("ExceptionTable", [
[u2be, "start_pc"],
[u2be, "end_pc"],
[u2be, "handler_pc"],
[u2be, "catch_type"]
]),
lengthValueEnum.value.value
)
),
"exception_table"
],
[
flatMapParser(u2be, (lengthValueEnum) =>
applyNTimes(parseAttributeInfo, lengthValueEnum.value.value)
),
"attributes"
]
])
Insert cell
parseConstantValueAttribute = createConstantInfoParser(
"ConstantValueAttribute",
[
[u2be, "constantvalue_index"] // Parses the constantvalue_index field as a 2-byte unsigned integer (big-endian)
]
)
Insert cell
parseVerificationTypeInfo /*mapParser(*/ = chainMapParser(u1, (was) => {
var val = was.value.value;
if (val == 7 || val == 8) {
return u2be;
}
return acceptAndDoNothing;
})
/*,
(was) => {
console.log(was);
return new ParseEnum(
"empty",
new ByteSlice(was.value.start, was.value.end, function () {
return null;
})
);
}
)*/
Insert cell
tagMap = function (tag, map) {
//We really don't care about the plumbing, just give us a tag and a value.
//map receives tag and value, that's it. it should return tag and value, why wouldn't the tag be important?
var [tag2, value2] = map(tag.tag, tag.value.value);
return {
tag: tag2,
value: { start: tag.value.start, end: tag.value.end, value: value2 }
};
}
Insert cell
flatMapOverSlices = function (parser, transformFn) {
//This one should take care automatically of plumbing and indexes the returned parser will be changed so the start index matches the first parser, user decides if wether to pass the value, this makes processing a bit more opaque, true, but I kinda regret taking care of start and end now (ByteSlices) anyway.
return function (index, bytes) {
// Apply the given parser first.
const result = parser(index, bytes);

// If the parser returns an error, propagate the error.
if (result.isErr()) {
return result.errMap((_) => ["flatMapOverSlices", _]);
}

// Extract the result value and next index.
/** @type {ParserNext<any>} */
const { value, nextIndex } = result.unwrap();
if (
!(Object.keys(value).length == 2 && "value" in value && "tag" in value)
) {
return Result.error([
"flatMapParser:Parser returns ok, but it must be object {tag,value}, returned: ",
value
]);
}
// Apply the transformation function to get the next parser.
const nextParser = transformFn(value);
const nextParsedResult = nextParser(nextIndex, bytes);
if (nextParsedResult.isErr()) {
//("Subsequent parser failed in bindParser following successful initial parse. Initial result: ", result, "Error in subsequent parser: ", nextParsedResult.error, "in string: ", remaining)
return nextParsedResult.errMap((_) => [
"flatMapParser: Initial result: ",
result,
"Error in subsequent parser: ",
_
]);
}
// console.log("setting the right result", result);
nextParsedResult.value.value.value.start = result.value.value.value.start;
return nextParsedResult;
};
}
Insert cell
parseStackMapFrame = flatMapOverSlices(u1, (frameType) => {
const frameTypeValue = frameType.value.value;
if (frameTypeValue >= 0 && frameTypeValue <= 63) {
return mapParser(acceptAndDoNothing, (_) => {
return tagMap(_, (t, v) => [
"same_frame",
{ offset_delta: frameTypeValue }
]);
});
} else if (frameTypeValue >= 64 && frameTypeValue <= 127) {
let offset_delta = frameTypeValue - 64;
return mapParser(parseVerificationTypeInfo, (_) => {
console.log(_);
return tagMap(_, (t, v) => [
"same_locals_1_stack_item_frame",
{ offset_delta, stack: v }
]);
});
} else if (frameTypeValue === 247) {
return createConstantInfoParser("same_locals_1_stack_item_frame_extended", [
[u2be, "offset_delta"],
[parseVerificationTypeInfo, "stack"]
]);
} else if (frameTypeValue >= 248 && frameTypeValue <= 250) {
// chop_frame
const k = 251 - frameTypeValue;
return mapParser(
createConstantInfoParser("chop_frame", [[u2be, "offset_delta"]]),
(_) => tagMap(_, (t, v) => [t, { offset_delta: v.offset_delta, k }])
);
} else if (frameTypeValue === 251) {
// same_frame_extended
return createConstantInfoParser("same_frame_extended", [
[u2be, "offset_delta"]
]);
} else if (frameTypeValue >= 252 && frameTypeValue <= 254) {
// append_frame
const k = frameTypeValue - 251;
return createConstantInfoParser("append_frame", [
// [constantParser(frameType), "frame_type"],
[u2be, "offset_delta"],
// [constantParser(k), "k"],
[applyNTimes(parseVerificationTypeInfo, k), "locals"]
]);
} else if (frameTypeValue === 255) {
// full_frame

return createConstantInfoParser("full_frame", [
[u2be, "offset_delta"],
[u2be, "number_of_locals"],
[
flatMapParser(u2be, (number_of_locals) =>
applyNTimes(parseVerificationTypeInfo, number_of_locals.value.value)
),
"locals"
],
[
flatMapParser(u2be, (number_of_stack_items) =>
applyNTimes(
parseVerificationTypeInfo,
number_of_stack_items.value.value
)
),
"stack"
]
]);
} else {
// Invalid frame type
return (_index, _bytes) =>
Result.error(`Invalid stack_map_frame type: ${frameTypeValue}`);
}
})
Insert cell
flatMapParser(u1, (tag) => {
switch (tag.value) {
case 0: // ITEM_Top
return Result.ok({ tag: "ITEM_Top" });

case 1: // ITEM_Integer
return Result.ok({ tag: "ITEM_Integer" });

case 2: // ITEM_Float
return Result.ok({ tag: "ITEM_Float" });

case 5: // ITEM_Null
return Result.ok({ tag: "ITEM_Null" });

case 6: // ITEM_UninitializedThis
return Result.ok({ tag: "ITEM_UninitializedThis" });

case 7: // ITEM_Object
return u2be(bytes).okMap((cpool_index) => ({
tag: "ITEM_Object",
cpool_index: cpool_index.value
}));

case 8: // ITEM_Uninitialized
return u2be(bytes).okMap((offset) => ({
tag: "ITEM_Uninitialized",
offset: offset.value
}));

case 4: // ITEM_Long
return Result.ok({ tag: "ITEM_Long" });

case 3: // ITEM_Double
return Result.ok({ tag: "ITEM_Double" });

default:
return Result.error(`Unknown verification_type_info tag: ${tag.value}`);
}
})(0, new Uint8Array([0]))
Insert cell
Insert cell
parseCode = function code(codeBA) {
//orParser([mapParser(expectP(u1).toBe(0x0),_=>{_.name="NOP";return _}), expectP(u1).toBe(0x1)]);
const Opcode = {
NOP: 0x0,
ACONST_NULL: 0x1,
ICONST_M1: 0x2,
ICONST_0: 0x3,
ICONST_1: 0x4,
ICONST_2: 0x5,
ICONST_3: 0x6,
ICONST_4: 0x7,
ICONST_5: 0x8,
LCONST_0: 0x9,
LCONST_1: 0xa,
FCONST_0: 0xb,
FCONST_1: 0xc,
FCONST_2: 0xd,
DCONST_0: 0xe,
DCONST_1: 0xf,
BIPUSH: 0x10,
SIPUSH: 0x11,
LDC: 0x12,
LDC_W: 0x13,
LDC2_W: 0x14,
ILOAD: 0x15,
LLOAD: 0x16,
FLOAD: 0x17,
DLOAD: 0x18,
ALOAD: 0x19,
ILOAD_0: 0x1a,
ILOAD_1: 0x1b,
ILOAD_2: 0x1c,
ILOAD_3: 0x1d,
LLOAD_0: 0x1e,
LLOAD_1: 0x1f,
LLOAD_2: 0x20,
LLOAD_3: 0x21,
FLOAD_0: 0x22,
FLOAD_1: 0x23,
FLOAD_2: 0x24,
FLOAD_3: 0x25,
DLOAD_0: 0x26,
DLOAD_1: 0x27,
DLOAD_2: 0x28,
DLOAD_3: 0x29,
ALOAD_0: 0x2a,
ALOAD_1: 0x2b,
ALOAD_2: 0x2c,
ALOAD_3: 0x2d,
IALOAD: 0x2e,
LALOAD: 0x2f,
FALOAD: 0x30,
DALOAD: 0x31,
AALOAD: 0x32,
BALOAD: 0x33,
CALOAD: 0x34,
SALOAD: 0x35,
ISTORE: 0x36,
LSTORE: 0x37,
FSTORE: 0x38,
DSTORE: 0x39,
ASTORE: 0x3a,
ISTORE_0: 0x3b,
ISTORE_1: 0x3c,
ISTORE_2: 0x3d,
ISTORE_3: 0x3e,
LSTORE_0: 0x3f,
LSTORE_1: 0x40,
LSTORE_2: 0x41,
LSTORE_3: 0x42,
FSTORE_0: 0x43,
FSTORE_1: 0x44,
FSTORE_2: 0x45,
FSTORE_3: 0x46,
DSTORE_0: 0x47,
DSTORE_1: 0x48,
DSTORE_2: 0x49,
DSTORE_3: 0x4a,
ASTORE_0: 0x4b,
ASTORE_1: 0x4c,
ASTORE_2: 0x4d,
ASTORE_3: 0x4e,
IASTORE: 0x4f,
LASTORE: 0x50,
FASTORE: 0x51,
DASTORE: 0x52,
AASTORE: 0x53,
BASTORE: 0x54,
CASTORE: 0x55,
SASTORE: 0x56,
POP: 0x57,
POP2: 0x58,
DUP: 0x59,
DUP_X1: 0x5a,
DUP_X2: 0x5b,
DUP2: 0x5c,
DUP2_X1: 0x5d,
DUP2_X2: 0x5e,
SWAP: 0x5f,
IADD: 0x60,
LADD: 0x61,
FADD: 0x62,
DADD: 0x63,
ISUB: 0x64,
LSUB: 0x65,
FSUB: 0x66,
DSUB: 0x67,
IMUL: 0x68,
LMUL: 0x69,
FMUL: 0x6a,
DMUL: 0x6b,
IDIV: 0x6c,
LDIV: 0x6d,
FDIV: 0x6e,
DDIV: 0x6f,
IREM: 0x70,
LREM: 0x71,
FREM: 0x72,
DREM: 0x73,
INEG: 0x74,
LNEG: 0x75,
FNEG: 0x76,
DNEG: 0x77,
ISHL: 0x78,
LSHL: 0x79,
ISHR: 0x7a,
LSHR: 0x7b,
IUSHR: 0x7c,
LUSHR: 0x7d,
IAND: 0x7e,
LAND: 0x7f,
IOR: 0x80,
LOR: 0x81,
IXOR: 0x82,
LXOR: 0x83,
IINC: 0x84,
I2L: 0x85,
I2F: 0x86,
I2D: 0x87,
L2I: 0x88,
L2F: 0x89,
L2D: 0x8a,
F2I: 0x8b,
F2L: 0x8c,
F2D: 0x8d,
D2I: 0x8e,
D2L: 0x8f,
D2F: 0x90,
I2B: 0x91,
I2C: 0x92,
I2S: 0x93,
LCMP: 0x94,
FCMPL: 0x95,
FCMPG: 0x96,
DCMPL: 0x97,
DCMPG: 0x98,
IFEQ: 0x99,
IFNE: 0x9a,
IFLT: 0x9b,
IFGE: 0x9c,
IFGT: 0x9d,
IFLE: 0x9e,
IF_ICMPEQ: 0x9f,
IF_ICMPNE: 0xa0,
IF_ICMPLT: 0xa1,
IF_ICMPGE: 0xa2,
IF_ICMPGT: 0xa3,
IF_ICMPLE: 0xa4,
IF_ACMPEQ: 0xa5,
IF_ACMPNE: 0xa6,
GOTO: 0xa7,
JSR: 0xa8,
RET: 0xa9,
TABLESWITCH: 0xaa,
LOOKUPSWITCH: 0xab,
IRETURN: 0xac,
LRETURN: 0xad,
FRETURN: 0xae,
DRETURN: 0xaf,
ARETURN: 0xb0,
RETURN: 0xb1,
GETSTATIC: 0xb2,
PUTSTATIC: 0xb3,
GETFIELD: 0xb4,
PUTFIELD: 0xb5,
INVOKEVIRTUAL: 0xb6,
INVOKESPECIAL: 0xb7,
INVOKESTATIC: 0xb8,
INVOKEINTERFACE: 0xb9,
INVOKEDYNAMIC: 0xba,
NEW: 0xbb,
NEWARRAY: 0xbc,
ANEWARRAY: 0xbd,
ARRAYLENGTH: 0xbe,
ATHROW: 0xbf,
CHECKCAST: 0xc0,
INSTANCEOF: 0xc1,
MONITORENTER: 0xc2,
MONITOREXIT: 0xc3,
WIDE: 0xc4,
MULTIANEWARRAY: 0xc5,
IFNULL: 0xc6,
IFNONNULL: 0xc7,
GOTO_W: 0xc8,
JSR_W: 0xc9,
BREAKPOINT: 0xca,
IMPDEP1: 0xfe,
IMPDEP2: 0xff
};

const parser = orParser([
mapParser(expectP(u1).toBe(Opcode.NOP), (_) => ({ ..._,name: "NOP", opcode: Opcode.NOP })),
mapParser(expectP(u1).toBe(Opcode.ACONST_NULL), (_) => ({ ..._,name: "ACONST_NULL", })),
mapParser(expectP(u1).toBe(Opcode.ICONST_M1), (_) => ({ ..._,name: "ICONST_M1", })),
mapParser(expectP(u1).toBe(Opcode.ICONST_0), (_) => ({ ..._,name: "ICONST_0", })),
mapParser(expectP(u1).toBe(Opcode.ICONST_1), (_) => ({ ..._,name: "ICONST_1", })),
mapParser(expectP(u1).toBe(Opcode.ICONST_2), (_) => ({ ..._,name: "ICONST_2", })),
mapParser(expectP(u1).toBe(Opcode.ICONST_3), (_) => ({ ..._,name: "ICONST_3", })),
mapParser(expectP(u1).toBe(Opcode.ICONST_4), (_) => ({ ..._,name: "ICONST_4", })),
mapParser(expectP(u1).toBe(Opcode.ICONST_5), (_) => ({ ..._,name: "ICONST_5", })),
mapParser(expectP(u1).toBe(Opcode.LCONST_0), (_) => ({ ..._,name: "LCONT_0", })),
mapParser(expectP(u1).toBe(Opcode.LCONST_1), (_) => ({ ..._,name: "LCONT_1", })),
mapParser(expectP(u1).toBe(Opcode.FCONST_0), (_) => ({ ..._,name: "FCONT_0", })),
mapParser(expectP(u1).toBe(Opcode.FCONST_1), (_) => ({ ..._,name: "FCONT_1", })),
mapParser(expectP(u1).toBe(Opcode.DCONST_0), (_) => ({..._, name: "DCONT_0", })),
mapParser(expectP(u1).toBe(Opcode.DCONST_1), (_) => ({..._,name: "DCONT_1", })),
// ...
]);
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more