Automatically detect Java entry points (#3614)

This commit is contained in:
Headline
2022-05-02 08:30:01 -07:00
committed by GitHub
parent a5e314aa5c
commit d68cc806b1

View File

@@ -26,11 +26,11 @@ import path from 'path';
import fs from 'fs-extra';
import { BaseCompiler } from '../base-compiler';
import { logger } from '../logger';
import {BaseCompiler} from '../base-compiler';
import {logger} from '../logger';
import * as utils from '../utils';
import { JavaParser } from './argument-parsers';
import {JavaParser} from './argument-parsers';
export class JavaCompiler extends BaseCompiler {
static get key() {
@@ -44,6 +44,7 @@ export class JavaCompiler extends BaseCompiler {
}
super(compilerInfo, env);
this.javaRuntime = this.compilerProps(`compiler.${this.compiler.id}.runtime`);
this.mainRegex = /public static ?(.*?) void main\(java\.lang\.String\[]\)/;
}
getSharedLibraryPathsAsArguments() {
@@ -54,33 +55,40 @@ export class JavaCompiler extends BaseCompiler {
const dirPath = path.dirname(outputFilename);
const files = await fs.readdir(dirPath);
logger.verbose('Class files: ', files);
const results = await Promise.all(files.filter(f => f.endsWith('.class')).map(async classFile => {
const args = [
// Prints out disassembled code, i.e., the instructions that comprise the Java bytecodes,
// for each of the methods in the class.
'-c',
// Prints out line and local variable tables.
'-l',
// Private things
'-p',
// Final constants
'-constants',
// Verbose - ideally we'd enable this and then we get constant pools too. Needs work to parse.
//'-v',
classFile,
];
const objResult = await this.exec(this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath});
const oneResult = {
asm: objResult.stdout,
};
const results = await Promise.all(
files
.filter(f => f.endsWith('.class'))
.map(async classFile => {
const args = [
// Prints out disassembled code, i.e., the instructions that comprise the Java bytecodes,
// for each of the methods in the class.
'-c',
// Prints out line and local variable tables.
'-l',
// Private things
'-p',
// Final constants
'-constants',
// Verbose - ideally we'd enable this and then we get constant pools too. Needs work to parse.
//'-v',
classFile,
];
const objResult = await this.exec(this.compiler.objdumper, args, {
maxOutput: maxSize,
customCwd: dirPath,
});
const oneResult = {
asm: objResult.stdout,
};
if (objResult.code !== 0) {
oneResult.asm = '<No output: javap returned ' + objResult.code + '>';
} else {
oneResult.objdumpTime = objResult.execTime;
}
return oneResult;
}));
if (objResult.code !== 0) {
oneResult.asm = '<No output: javap returned ' + objResult.code + '>';
} else {
oneResult.objdumpTime = objResult.execTime;
}
return oneResult;
}),
);
const merged = {asm: []};
for (const result of results) {
@@ -98,11 +106,7 @@ export class JavaCompiler extends BaseCompiler {
// Forcibly enable javap
filters.binary = true;
return [
'-Xlint:all',
'-encoding',
'utf8',
];
return ['-Xlint:all', '-encoding', 'utf8'];
}
async handleInterpreting(key, executeParameters) {
@@ -113,7 +117,7 @@ export class JavaCompiler extends BaseCompiler {
'-XX:-UseDynamicNumberOfCompilerThreads',
'-XX:-UseDynamicNumberOfGCThreads',
'-XX:+UseSerialGC', // Disable parallell/concurrent garbage collector
this.getMainClassName(),
await this.getMainClassName(compileResult.dirPath),
'-cp',
compileResult.dirPath,
...executeParameters.args,
@@ -124,8 +128,36 @@ export class JavaCompiler extends BaseCompiler {
return result;
}
getMainClassName() {
// TODO(supergrecko): The main class name is currently hardcoded
async getMainClassName(dirPath) {
const maxSize = this.env.ceProps('max-asm-size', 64 * 1024 * 1024);
const files = await fs.readdir(dirPath);
const results = await Promise.all(
files
.filter(f => f.endsWith('.class'))
.map(async classFile => {
const options = {
maxOutput: maxSize,
customCwd: dirPath,
};
const objResult = await this.exec(this.compiler.objdumper, [classFile], options);
if (objResult.code !== 0) {
return null;
}
if (this.mainRegex.test(objResult.stdout)) {
return classFile;
}
return null;
}),
);
const candidates = results.filter(file => file !== null);
if (candidates.length > 0) {
// In case of multiple candidates, we'll just take the first one.
const fileName = candidates[0];
return fileName.substring(0, fileName.lastIndexOf('.'));
}
// We were unable to find a main method, let's error out assuming "Main"
return 'Main';
}
@@ -167,7 +199,8 @@ export class JavaCompiler extends BaseCompiler {
'-s',
// --source-path path or -sourcepath path
// Specifies where to find input source files.
'--source-path', '-sourcepath',
'--source-path',
'-sourcepath',
]);
return this.filterUserOptionsWithArg(userOptions, oneArgForbiddenList);
@@ -238,7 +271,7 @@ export class JavaCompiler extends BaseCompiler {
for (const codeLineCandidate of utils.splitLines(codeAndLineNumberTable)) {
// Match
// 1: invokespecial #1 // Method java/lang/Object."<init>":()V
// Or match the "default: <code>" block inside a lookupswitch instruction
// Or match the "default: <code>" block inside a lookupswitch instruction
const match = codeLineCandidate.match(/\s+(\d+|default): (.*)/);
if (match) {
const instrOffset = Number.parseInt(match[1]);
@@ -281,8 +314,10 @@ export class JavaCompiler extends BaseCompiler {
// Some instructions don't receive an explicit line number.
// They are all assigned to the previous explicit line number,
// because the line consists of multiple instructions.
while (currentInstr < method.instructions.length
&& method.instructions[currentInstr].instrOffset !== instrOffset) {
while (
currentInstr < method.instructions.length &&
method.instructions[currentInstr].instrOffset !== instrOffset
) {
if (currentSourceLine !== -1) {
// instructions without explicit line number get assigned the last explicit/same line number
method.instructions[currentInstr].sourceLine = currentSourceLine;
@@ -320,7 +355,7 @@ export class JavaCompiler extends BaseCompiler {
}
return {
// Used for sorting
firstSourceLine: methods.reduce((p, m) => p === -1 ? m.startLine : Math.min(p, m.startLine), -1),
firstSourceLine: methods.reduce((p, m) => (p === -1 ? m.startLine : Math.min(p, m.startLine)), -1),
methods: methods,
textsBeforeMethod,
};