Skip to content

Commit

Permalink
Add instruction verifying for writing class files
Browse files Browse the repository at this point in the history
* introduced `InstructionMajorVersionVerifier` for verifying instructions
  before writing in `ClassWriter#verify`

refs #8
  • Loading branch information
TSedlar authored and kylestev committed Nov 3, 2015
1 parent 636aa24 commit 07f509c
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 2 deletions.
7 changes: 5 additions & 2 deletions src/core/io/writers/ClassWriter.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { InstructionMajorVersionVerifier } from '../../jvm/verify/InstructionMajorVersionVerifier';
const JVM_CLASS_FILE_MAGIC_NUMBER = 0xcafebabe;

class ClassWriter {
constructor(cls, buffer) {
this.cls = cls;
this.buffer = buffer;
this.majorVerifier = new InstructionMajorVersionVerifier(cls);
}

verify() {
// will need to change to ensure all verifiers pass before returning once they are added
return true;
return this.majorVerifier.verify();
}

write() {
if ( ! this.verify()) {
if (!this.verify()) {
let major = this.cls.major;
throw new Error('Unable to verify class contents for writing: ' + this.cls.name);
}

Expand Down
5 changes: 5 additions & 0 deletions src/core/jvm/instructions/Opcodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ export const NAME_TO_OPCODE = _.object(_.map(_.invert(OPCODE_TO_NAME), (opcode,
return [name, +opcode];
}));

export const OPCODE_VERSIONS = {
// JDK 7
51: [NAME_TO_OPCODE.INVOKEDYNAMIC]
};

export const INSTRUCTION_INDICES = [
{ // ICONST_M1 - DCONST_1
low_opcode_index: 0x02,
Expand Down
36 changes: 36 additions & 0 deletions src/core/jvm/verify/InstructionMajorVersionVerifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as _ from 'lodash';
import { OPCODE_VERSIONS } from '../instructions/Opcodes';
const WritingErrors = require('../../../errors').Writing;

class InstructionMajorVersionVerifier {

constructor(classInfo) {
this.classInfo = classInfo;
this.unusableOpcodes = this.findUnusableOpcodes();
}

findUnusableOpcodes() {
return _.flatten(_.filter(OPCODE_VERSIONS, (opcodes, version) => {
return version > this.classInfo.major;
}));
}

findInvalidMethodOpcodes(method) {
let opcodes = _.pluck(method.instructions, 'opcode');
return _.intersection(opcodes, this.unusableOpcodes);
}

verify() {
return _.every(this.classInfo.methods, (method) => {
let invalid = this.findInvalidMethodOpcodes(method);
if (invalid.length > 0) {
throw WritingErrors.UnsupportedOpcode(unusable[0], this.classInfo, method);
}
return true;
});
}
}

export default {
InstructionMajorVersionVerifier
};
8 changes: 8 additions & 0 deletions src/errors/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { OPCODE_TO_NAME } from '../core/jvm/instructions/Opcodes';

export default {
Parsing: {
InvalidClassFileFormat: new Error('Invalid class file format!')
},
Writing: {
UnsupportedOpcode: (opcode, classInfo, method) => {
return new Error(OPCODE_TO_NAME[opcode] + ' is not supported on major ' + classInfo.major +
' in ' + classInfo.name + '#' + method.name + method.desc);
}
},
NotImplementedError: new Error('This has not yet been implemented.')
}

0 comments on commit 07f509c

Please sign in to comment.