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

fix(java): compilation fails with "code too large" #1605

Merged
merged 1 commit into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 82 additions & 7 deletions packages/jsii-pacmak/lib/targets/java.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,20 @@ class JavaGenerator extends Generator {
return `${path.join('src', 'main', 'java', ...packageName.split('.'), typeName.split('.')[0])}.java`;
}

private toJavaResourcePath(assm: spec.Assembly, fqn: string, ext = '.txt') {
const { packageName, typeName } = this.toNativeName(assm, {
fqn,
kind: spec.TypeKind.Class,
assembly: assm.name,
name: fqn.replace(/.*\.([^.]+)$/, '$1')
});

const name = `${path.join(...packageName.split('.'), typeName.split('.')[0])}${ext}`;
const filePath = path.join('src', 'main', 'resources', name);

return { filePath, name };
}

// eslint-disable-next-line complexity
private addJavaDocs(doc: spec.Documentable, defaultText?: string) {
if (!defaultText && Object.keys(doc.docs ?? {}).length === 0
Expand Down Expand Up @@ -1813,24 +1827,74 @@ class JavaGenerator extends Generator {
private emitModuleFile(mod: spec.Assembly) {
const moduleName = mod.name;
const moduleClass = this.makeModuleClass(moduleName);

const { filePath: moduleResFile, name: moduleResName } = this.toJavaResourcePath(mod, `${mod.name}.${MODULE_CLASS_NAME}`);
this.code.openFile(moduleResFile);
for (const fqn of Object.keys(this.assembly.types ?? {})) {
this.code.line(`${fqn}=${this.toNativeFqn(fqn, { binaryName: true })}`);
}
this.code.closeFile(moduleResFile);

const moduleFile = this.toJavaFilePath(mod, {
assembly: mod.name,
fqn: `${mod.name}.${MODULE_CLASS_NAME}`,
kind: spec.TypeKind.Class,
name: MODULE_CLASS_NAME,
});

this.code.openFile(moduleFile);
this.code.line(`package ${this.toNativeName(mod).packageName};`);
this.code.line();
if (Object.keys(mod.dependencies ?? {}).length > 0) {
this.code.line('import static java.util.Arrays.asList;');
this.code.line();
}
this.code.line('import java.io.BufferedReader;');
this.code.line('import java.io.InputStream;');
this.code.line('import java.io.InputStreamReader;');
this.code.line('import java.io.IOException;');
this.code.line('import java.io.Reader;');
this.code.line('import java.io.UncheckedIOException;');
this.code.line();
this.code.line('import java.nio.charset.StandardCharsets;');
this.code.line();
this.code.line('import java.util.HashMap;');
if (Object.keys(mod.dependencies ?? {}).length > 0) {
this.code.line('import java.util.List;');
}
this.code.line('import java.util.Map;');
this.code.line();
this.code.line('import software.amazon.jsii.JsiiModule;');
this.code.line();

this.code.openBlock(`public final class ${MODULE_CLASS_NAME} extends JsiiModule`);
this.code.line('private static final Map<String, String> MODULE_TYPES = load();');
this.code.line();

this.code.openBlock('private static Map<String, String> load()');
this.code.line('final Map<String, String> result = new HashMap<>();');
this.code.line(`final ClassLoader cl = ${MODULE_CLASS_NAME}.class.getClassLoader();`);
this.code.line(`try (final InputStream is = cl.getResourceAsStream("${moduleResName}");`);
this.code.line(' final Reader rd = new InputStreamReader(is, StandardCharsets.UTF_8);');
this.code.openBlock(' final BufferedReader br = new BufferedReader(rd))');
this.code.line('br.lines()');
this.code.line(' .filter(line -> !line.trim().isEmpty())');
this.code.openBlock(' .forEach(line -> ');
this.code.line('final String[] parts = line.split("=", 2);');
this.code.line('final String fqn = parts[0];');
this.code.line('final String className = parts[1];');
this.code.line('result.put(fqn, className);');
this.code.unindent('});'); // Proxy for closeBlock
this.code.closeBlock();
this.code.openBlock('catch (final IOException exception)');
this.code.line('throw new UncheckedIOException(exception);');
this.code.closeBlock();
this.code.line('return result;');
this.code.closeBlock();
this.code.line();

this.code.line('private final Map<String, Class<?>> cache = new HashMap<>();');
this.code.line();

// ctor
this.code.openBlock(`public ${MODULE_CLASS_NAME}()`);
Expand All @@ -1854,11 +1918,19 @@ class JavaGenerator extends Generator {
this.code.line();
this.code.line('@Override');
this.code.openBlock('protected Class<?> resolveClass(final String fqn) throws ClassNotFoundException');
this.code.openBlock('switch (fqn)');
for (const type of Object.keys(this.assembly.types ?? {})) {
this.code.line(`case "${type}": return ${this.toNativeFqn(type)}.class;`);
}
this.code.line('default: throw new ClassNotFoundException("Unknown JSII type: " + fqn);');
this.code.openBlock('if (!MODULE_TYPES.containsKey(fqn))');
this.code.line('throw new ClassNotFoundException("Unknown JSII type: " + fqn);');
this.code.closeBlock();
this.code.line('return this.cache.computeIfAbsent(MODULE_TYPES.get(fqn), this::findClass);');
this.code.closeBlock();

this.code.line();
this.code.openBlock('private Class<?> findClass(final String binaryName)');
this.code.openBlock('try');
this.code.line('return Class.forName(binaryName);');
this.code.closeBlock();
this.code.openBlock('catch (final ClassNotFoundException exception)');
this.code.line('throw new RuntimeException(exception);');
this.code.closeBlock();
this.code.closeBlock();

Expand Down Expand Up @@ -1893,7 +1965,7 @@ class JavaGenerator extends Generator {
*
* @throws if the assembly the FQN belongs to does not have a `targets.java.package` set.
*/
private toNativeFqn(fqn: string): string {
private toNativeFqn(fqn: string, { binaryName }: { binaryName: boolean } = { binaryName: false }): string {
const [mod, ...name] = fqn.split('.');
const depMod = this.findModule(mod);
// Make sure any dependency (direct or transitive) of which any type is explicitly referenced by the generated
Expand All @@ -1905,7 +1977,10 @@ class JavaGenerator extends Generator {
}

const { packageName, typeName } = this.toNativeName(this.assembly, this.assembly.types![fqn]);
return `${packageName}${typeName ? `.${typeName}` : ''}`;
const className = typeName && binaryName
? typeName.replace('.', '$')
: typeName;
return `${packageName}${className ? `.${className}` : ''}`;
}

private getNativeName(assm: spec.Assembly, name: string | undefined): string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,63 @@
package software.amazon.jsii.tests.calculator.baseofbase;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;

import java.nio.charset.StandardCharsets;

import java.util.HashMap;
import java.util.Map;

import software.amazon.jsii.JsiiModule;

public final class $Module extends JsiiModule {
private static final Map<String, String> MODULE_TYPES = load();

private static Map<String, String> load() {
final Map<String, String> result = new HashMap<>();
final ClassLoader cl = $Module.class.getClassLoader();
try (final InputStream is = cl.getResourceAsStream("software/amazon/jsii/tests/calculator/baseofbase/$Module.txt");
final Reader rd = new InputStreamReader(is, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(rd)) {
br.lines()
.filter(line -> !line.trim().isEmpty())
.forEach(line -> {
final String[] parts = line.split("=", 2);
final String fqn = parts[0];
final String className = parts[1];
result.put(fqn, className);
});
}
catch (final IOException exception) {
throw new UncheckedIOException(exception);
}
return result;
}

private final Map<String, Class<?>> cache = new HashMap<>();

public $Module() {
super("@scope/jsii-calc-base-of-base", "0.0.0", $Module.class, "[email protected]");
}

@Override
protected Class<?> resolveClass(final String fqn) throws ClassNotFoundException {
switch (fqn) {
case "@scope/jsii-calc-base-of-base.IVeryBaseInterface": return software.amazon.jsii.tests.calculator.baseofbase.IVeryBaseInterface.class;
case "@scope/jsii-calc-base-of-base.Very": return software.amazon.jsii.tests.calculator.baseofbase.Very.class;
case "@scope/jsii-calc-base-of-base.VeryBaseProps": return software.amazon.jsii.tests.calculator.baseofbase.VeryBaseProps.class;
default: throw new ClassNotFoundException("Unknown JSII type: " + fqn);
if (!MODULE_TYPES.containsKey(fqn)) {
throw new ClassNotFoundException("Unknown JSII type: " + fqn);
}
return this.cache.computeIfAbsent(MODULE_TYPES.get(fqn), this::findClass);
}

private Class<?> findClass(final String binaryName) {
try {
return Class.forName(binaryName);
}
catch (final ClassNotFoundException exception) {
throw new RuntimeException(exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@scope/jsii-calc-base-of-base.IVeryBaseInterface=software.amazon.jsii.tests.calculator.baseofbase.IVeryBaseInterface
@scope/jsii-calc-base-of-base.Very=software.amazon.jsii.tests.calculator.baseofbase.Very
@scope/jsii-calc-base-of-base.VeryBaseProps=software.amazon.jsii.tests.calculator.baseofbase.VeryBaseProps
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,47 @@

import static java.util.Arrays.asList;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;

import java.nio.charset.StandardCharsets;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import software.amazon.jsii.JsiiModule;

public final class $Module extends JsiiModule {
private static final Map<String, String> MODULE_TYPES = load();

private static Map<String, String> load() {
final Map<String, String> result = new HashMap<>();
final ClassLoader cl = $Module.class.getClassLoader();
try (final InputStream is = cl.getResourceAsStream("software/amazon/jsii/tests/calculator/base/$Module.txt");
final Reader rd = new InputStreamReader(is, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(rd)) {
br.lines()
.filter(line -> !line.trim().isEmpty())
.forEach(line -> {
final String[] parts = line.split("=", 2);
final String fqn = parts[0];
final String className = parts[1];
result.put(fqn, className);
});
}
catch (final IOException exception) {
throw new UncheckedIOException(exception);
}
return result;
}

private final Map<String, Class<?>> cache = new HashMap<>();

public $Module() {
super("@scope/jsii-calc-base", "0.0.0", $Module.class, "[email protected]");
}
Expand All @@ -17,11 +54,18 @@ public List<Class<? extends JsiiModule>> getDependencies() {

@Override
protected Class<?> resolveClass(final String fqn) throws ClassNotFoundException {
switch (fqn) {
case "@scope/jsii-calc-base.Base": return software.amazon.jsii.tests.calculator.base.Base.class;
case "@scope/jsii-calc-base.BaseProps": return software.amazon.jsii.tests.calculator.base.BaseProps.class;
case "@scope/jsii-calc-base.IBaseInterface": return software.amazon.jsii.tests.calculator.base.IBaseInterface.class;
default: throw new ClassNotFoundException("Unknown JSII type: " + fqn);
if (!MODULE_TYPES.containsKey(fqn)) {
throw new ClassNotFoundException("Unknown JSII type: " + fqn);
}
return this.cache.computeIfAbsent(MODULE_TYPES.get(fqn), this::findClass);
}

private Class<?> findClass(final String binaryName) {
try {
return Class.forName(binaryName);
}
catch (final ClassNotFoundException exception) {
throw new RuntimeException(exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@scope/jsii-calc-base.Base=software.amazon.jsii.tests.calculator.base.Base
@scope/jsii-calc-base.BaseProps=software.amazon.jsii.tests.calculator.base.BaseProps
@scope/jsii-calc-base.IBaseInterface=software.amazon.jsii.tests.calculator.base.IBaseInterface
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,47 @@

import static java.util.Arrays.asList;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;

import java.nio.charset.StandardCharsets;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import software.amazon.jsii.JsiiModule;

public final class $Module extends JsiiModule {
private static final Map<String, String> MODULE_TYPES = load();

private static Map<String, String> load() {
final Map<String, String> result = new HashMap<>();
final ClassLoader cl = $Module.class.getClassLoader();
try (final InputStream is = cl.getResourceAsStream("software/amazon/jsii/tests/calculator/lib/$Module.txt");
final Reader rd = new InputStreamReader(is, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(rd)) {
br.lines()
.filter(line -> !line.trim().isEmpty())
.forEach(line -> {
final String[] parts = line.split("=", 2);
final String fqn = parts[0];
final String className = parts[1];
result.put(fqn, className);
});
}
catch (final IOException exception) {
throw new UncheckedIOException(exception);
}
return result;
}

private final Map<String, Class<?>> cache = new HashMap<>();

public $Module() {
super("@scope/jsii-calc-lib", "0.0.0", $Module.class, "[email protected]");
}
Expand All @@ -17,17 +54,18 @@ public List<Class<? extends JsiiModule>> getDependencies() {

@Override
protected Class<?> resolveClass(final String fqn) throws ClassNotFoundException {
switch (fqn) {
case "@scope/jsii-calc-lib.EnumFromScopedModule": return software.amazon.jsii.tests.calculator.lib.EnumFromScopedModule.class;
case "@scope/jsii-calc-lib.IDoublable": return software.amazon.jsii.tests.calculator.lib.IDoublable.class;
case "@scope/jsii-calc-lib.IFriendly": return software.amazon.jsii.tests.calculator.lib.IFriendly.class;
case "@scope/jsii-calc-lib.IThreeLevelsInterface": return software.amazon.jsii.tests.calculator.lib.IThreeLevelsInterface.class;
case "@scope/jsii-calc-lib.MyFirstStruct": return software.amazon.jsii.tests.calculator.lib.MyFirstStruct.class;
case "@scope/jsii-calc-lib.Number": return software.amazon.jsii.tests.calculator.lib.Number.class;
case "@scope/jsii-calc-lib.Operation": return software.amazon.jsii.tests.calculator.lib.Operation.class;
case "@scope/jsii-calc-lib.StructWithOnlyOptionals": return software.amazon.jsii.tests.calculator.lib.StructWithOnlyOptionals.class;
case "@scope/jsii-calc-lib.Value": return software.amazon.jsii.tests.calculator.lib.Value.class;
default: throw new ClassNotFoundException("Unknown JSII type: " + fqn);
if (!MODULE_TYPES.containsKey(fqn)) {
throw new ClassNotFoundException("Unknown JSII type: " + fqn);
}
return this.cache.computeIfAbsent(MODULE_TYPES.get(fqn), this::findClass);
}

private Class<?> findClass(final String binaryName) {
try {
return Class.forName(binaryName);
}
catch (final ClassNotFoundException exception) {
throw new RuntimeException(exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@scope/jsii-calc-lib.EnumFromScopedModule=software.amazon.jsii.tests.calculator.lib.EnumFromScopedModule
@scope/jsii-calc-lib.IDoublable=software.amazon.jsii.tests.calculator.lib.IDoublable
@scope/jsii-calc-lib.IFriendly=software.amazon.jsii.tests.calculator.lib.IFriendly
@scope/jsii-calc-lib.IThreeLevelsInterface=software.amazon.jsii.tests.calculator.lib.IThreeLevelsInterface
@scope/jsii-calc-lib.MyFirstStruct=software.amazon.jsii.tests.calculator.lib.MyFirstStruct
@scope/jsii-calc-lib.Number=software.amazon.jsii.tests.calculator.lib.Number
@scope/jsii-calc-lib.Operation=software.amazon.jsii.tests.calculator.lib.Operation
@scope/jsii-calc-lib.StructWithOnlyOptionals=software.amazon.jsii.tests.calculator.lib.StructWithOnlyOptionals
@scope/jsii-calc-lib.Value=software.amazon.jsii.tests.calculator.lib.Value
Loading