Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jvm instruction tests #15

Merged
merged 13 commits into from
Feb 15, 2024
Prev Previous commit
Next Next commit
ts fix
  • Loading branch information
Chang-CH committed Nov 29, 2023
commit 17e5b2e091940d566d488c71183dbcdbb49c5fa0
171 changes: 20 additions & 151 deletions src/jvm/ClassLoader/AbstractClassLoader.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { ClassFile } from "../../ClassFile/types";
import { CodeAttribute } from "../../ClassFile/types/attributes";
import { ConstantInfo, ConstantUtf8Info, ConstantClassInfo } from "../../ClassFile/types/constants";
import { MethodInfo } from "../../ClassFile/types/methods";
import AbstractSystem from "../utils/AbstractSystem";
import { ClassData } from "../types/class/ClassData";
import { MethodHandler } from "../types/class/Method";
import { ConstantClassInfo, ConstantUtf8Info } from "../../ClassFile/types/constants";
import { ErrorResult, ImmediateResult, checkError, checkSuccess } from "../types/Result";
import { ClassData, ReferenceClassData, ArrayClassData, PrimitiveClassData } from "../types/class/ClassData";
import { JvmObject } from "../types/reference/Object";
import { ImmediateResult, checkError, checkSuccess } from "../utils/Result";
import { ACCESS_FLAGS as CLASS_FLAGS } from "../../ClassFile/types";
import AbstractSystem from "../utils/AbstractSystem";


export default abstract class AbstractClassLoader {
Expand Down Expand Up @@ -38,155 +34,31 @@ export default abstract class AbstractClassLoader {
return;
}

private _linkMethod(
constantPool: ConstantInfo[],
method: MethodInfo
): ImmediateResult<{
method: MethodInfo;
exceptionHandlers: MethodHandler[];
code: CodeAttribute | null;
}> {
// get code attribute
let code: CodeAttribute | null = null;
for (const attr of method.attributes) {
const attrname = (
constantPool[attr.attributeNameIndex] as ConstantUtf8Info
).value;
if (attrname === 'Code') {
code = attr as CodeAttribute;
}
}

const handlderTable: MethodHandler[] = [];
if (code) {
for (const handler of code.exceptionTable) {
const ctIndex = handler.catchType;
if (ctIndex === 0) {
handlderTable.push({
startPc: handler.startPc,
endPc: handler.endPc,
handlerPc: handler.handlerPc,
catchType: null,
});
continue;
}

const catchType = constantPool[
(constantPool[ctIndex] as ConstantClassInfo).nameIndex
] as ConstantUtf8Info;
const ctRes = this.getClassRef(catchType.value);
if (checkError(ctRes)) {
return { exceptionCls: 'java/lang/NoClassDefFoundError', msg: '' };
}
const clsRef = ctRes.result;

handlderTable.push({
startPc: handler.startPc,
endPc: handler.endPc,
handlerPc: handler.handlerPc,
catchType: clsRef,
});
}
}

return {
result: {
method,
exceptionHandlers: handlderTable,
code,
},
};
}

/**
* Resolves symbolic references in the constant pool.
* @param cls class data to resolve
* @returns class data with resolved references
* Loads superclasses etc. for reference classes
*/
protected linkClass(cls: ClassFile): ClassData {
const constantPool = cls.constantPool;
const accessFlags = cls.accessFlags;

protected linkClass(cls: ClassFile): ReferenceClassData {
// resolve classname
const clsInfo = cls.constantPool[cls.thisClass] as ConstantClassInfo;
const clsName = cls.constantPool[clsInfo.nameIndex] as ConstantUtf8Info;
const thisClass = clsName.value;

// resolve superclass
let superClass = null;
if (cls.superClass !== 0) {
const superClassIndex = cls.constantPool[
cls.superClass
] as ConstantClassInfo;
const superClassName = cls.constantPool[
superClassIndex.nameIndex
] as ConstantUtf8Info;
const res = this.getClassRef(superClassName.value);

if (checkError(res)) {
throw new Error(res.exceptionCls);
}

superClass = res.result;
}

if ((accessFlags & CLASS_FLAGS.ACC_INTERFACE) !== 0 && !superClass) {
// Some compilers set superclass to object by default.
// We force it to be java/lang/Object if it's not set.
// assume object is loaded at initialization.
superClass = (this.getClassRef('java/lang/Object') as any)
.result as ClassData;
}

// resolve interfaces
const interfaces: ClassData[] = [];
cls.interfaces.forEach(interfaceIndex => {
const interfaceNameIdx = (
cls.constantPool[interfaceIndex] as ConstantClassInfo
).nameIndex;
const interfaceName = (
cls.constantPool[interfaceNameIdx] as ConstantUtf8Info
).value;
const res = this.getClassRef(interfaceName);
if (checkError(res)) {
throw new Error(res.exceptionCls);
}
interfaces.push(res.result);
});

const methods: {
method: MethodInfo;
exceptionHandlers: MethodHandler[];
code: CodeAttribute | null;
}[] = [];
cls.methods.forEach(method => {
const res = this._linkMethod(constantPool, method);
if (checkError(res)) {
throw new Error(res.exceptionCls);
}
const mData = res.result;
methods.push(mData);
});

const attributes = cls.attributes;

const data = new ClassData(
constantPool,
accessFlags,
let hasError: ErrorResult | null = null;
const data = new ReferenceClassData(
cls,
this,
thisClass,
superClass,
interfaces,
cls.fields,
methods,
attributes,
this
e => (hasError = e)
);

if (hasError) {
throw new Error((hasError as ErrorResult).exceptionCls);
}
return data;
}

/**
* Adds the resolved class data to the memory area.
* @param cls resolved class data
* Stores the resolved class data.
*/
protected loadClass(cls: ClassData): ClassData {
this.loadedClasses[cls.getClassname()] = cls;
Expand All @@ -196,8 +68,7 @@ export default abstract class AbstractClassLoader {
protected _loadArrayClass(
className: string,
componentCls: ClassData
): ImmediateResult<ClassData> {
// array classes should be loaded by bootstrap loader
): ImmediateResult<ArrayClassData> {
if (!this.parentLoader) {
throw new Error('ClassLoader has no parent loader');
}
Expand Down Expand Up @@ -250,10 +121,8 @@ export default abstract class AbstractClassLoader {
}

/**
* Gets the class data given the classname, loads the class if not loaded.
*
* @param className
* @returns
* Gets the reference class data given the classname, loads the class if not loaded.
* Not for primitive classes, use getPrimitiveClassRef for primitive classes.
*/
getClassRef(className: string): ImmediateResult<ClassData> {
return this._getClassRef(className, this);
Expand All @@ -264,7 +133,7 @@ export default abstract class AbstractClassLoader {
* @throws Error if class is not a primitive
* @param className
*/
abstract getPrimitiveClassRef(className: string): ClassData;
abstract getPrimitiveClassRef(className: string): PrimitiveClassData;

protected abstract load(className: string): ImmediateResult<ClassData>;

Expand Down
74 changes: 27 additions & 47 deletions src/jvm/ClassLoader/BootstrapClassLoader.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import AbstractSystem from "../utils/AbstractSystem";
import { ArrayClassData } from "../types/class/ArrayClassData";
import { ClassData, CLASS_TYPE } from "../types/class/ClassData";
import { ImmediateResult, ErrorResult } from "../types/Result";
import { PrimitiveClassData, ClassData, ArrayClassData } from "../types/class/ClassData";
import { JavaType, JvmObject } from "../types/reference/Object";
import { primitiveTypeToName } from "../utils";
import { ImmediateResult, checkError } from "../utils/Result";
import AbstractSystem from "../utils/AbstractSystem";
import AbstractClassLoader from "./AbstractClassLoader";
import { ACCESS_FLAGS as CLASS_FLAGS } from "../../ClassFile/types";

/**
* Reads classfile representation and loads it into memory area
*/
export default class BootstrapClassLoader extends AbstractClassLoader {
private primitiveClasses: { [className: string]: ClassData } = {};
private primitiveClasses: { [className: string]: PrimitiveClassData } = {};

constructor(nativeSystem: AbstractSystem, classPath: string) {
super(nativeSystem, classPath, null);
Expand All @@ -20,34 +19,18 @@ export default class BootstrapClassLoader extends AbstractClassLoader {
private loadArray(
className: string,
componentCls: ClassData
): ImmediateResult<ClassData> {
// #region load array superclasses/interfaces
const objRes = this.getClassRef('java/lang/Object');
if (checkError(objRes)) {
return objRes;
}
const cloneableRes = this.getClassRef('java/lang/Cloneable');
if (checkError(cloneableRes)) {
return cloneableRes;
}
const serialRes = this.getClassRef('java/io/Serializable');
if (checkError(serialRes)) {
return serialRes;
}
// #endregion

): ImmediateResult<ArrayClassData> {
let error: ErrorResult | null = null;
const arrayClass = new ArrayClassData(
[],
CLASS_FLAGS.ACC_PUBLIC,
className,
objRes.result,
[cloneableRes.result, serialRes.result],
[],
[],
[],
this
this,
componentCls,
e => (error = e)
);
arrayClass.setComponentClass(componentCls);
if (error) {
return error;
}

this.loadClass(arrayClass);
return { result: arrayClass };
Expand All @@ -58,6 +41,8 @@ export default class BootstrapClassLoader extends AbstractClassLoader {
* @param className name of class to load
*/
protected load(className: string): ImmediateResult<ClassData> {
console.debug(`BsCL: loading ${className}`);

const path = this.classPath ? this.classPath + '/' + className : className;

let classFile;
Expand All @@ -78,32 +63,27 @@ export default class BootstrapClassLoader extends AbstractClassLoader {
protected _loadArrayClass(
className: string,
componentCls: ClassData
): ImmediateResult<ClassData> {
): ImmediateResult<ArrayClassData> {
return this.loadArray(className, componentCls);
}

getPrimitiveClassRef(className: string): ClassData {
if (this.primitiveClasses[className]) {
return this.primitiveClasses[className];
}
getPrimitiveClassRef(className: string): PrimitiveClassData {
const internalName = primitiveTypeToName(className as JavaType);
if (!internalName) {
throw new Error(`Invalid primitive class name: ${className}`);
}

const cls = new ClassData(
[],
CLASS_FLAGS.ACC_PUBLIC,
internalName,
null,
[],
[],
[],
[],
this,
CLASS_TYPE.PRIMITIVE
);
this.primitiveClasses[className] = cls;
if (this.primitiveClasses[internalName]) {
return this.primitiveClasses[internalName];
}

const cls = new PrimitiveClassData(this, internalName);
this.primitiveClasses[internalName] = cls;

if (internalName === 'char') {
console.log('CHAR PRIMITIVE LOADED');
}

return cls;
}

Expand Down
Loading