Large diffs are not rendered by default.

@@ -67,7 +67,7 @@
</archive>
</configuration>
</plugin>
<plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
@@ -119,4 +119,26 @@
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>target15</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerId>javac</compilerId>
<source>8</source>
<testSource>1.5</testSource>
<testTarget>1.5</testTarget>
<compilerArgs>
<arg>-Xlint:deprecation</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
@@ -0,0 +1,128 @@
/**
* Back 2 Browser Bytecode Translator
* Copyright (C) 2012-2018 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://opensource.org/licenses/GPL-2.0.
*/
package org.apidesign.vm4brwsr;

import java.io.IOException;
import org.apidesign.vm4brwsr.ByteCodeParser.TypeArray;

abstract class AbstractStackMapper {
public abstract void clear();

public abstract void syncWithFrameStack(final TypeArray frameStack);

public final CharSequence pushI() {
return pushT(VarType.INTEGER);
}

public final CharSequence pushL() {
return pushT(VarType.LONG);
}

public final CharSequence pushF() {
return pushT(VarType.FLOAT);
}

public final CharSequence pushD() {
return pushT(VarType.DOUBLE);
}

public final CharSequence pushA() {
return pushT(VarType.REFERENCE);
}

public abstract CharSequence pushT(final int type);

abstract void assign(Appendable out, int varType, CharSequence s) throws IOException;

abstract void replace(Appendable out, int varType, String format, CharSequence... arr)
throws IOException;

abstract void flush(Appendable out) throws IOException;

public abstract boolean isDirty();

public final CharSequence popI(Appendable out) throws IOException {
return popT(out, VarType.INTEGER);
}

public final CharSequence popL(Appendable out) throws IOException {
return popT(out, VarType.LONG);
}

public final CharSequence popF(Appendable out) throws IOException {
return popT(out, VarType.FLOAT);
}

public final CharSequence popD(Appendable out) throws IOException {
return popT(out, VarType.DOUBLE);
}

public final CharSequence popA(Appendable out) throws IOException {
return popT(out, VarType.REFERENCE);
}

public abstract CharSequence popT(Appendable out, final int type) throws IOException;

public abstract CharSequence popValue(Appendable out) throws IOException;

public abstract Variable pop(Appendable out) throws IOException;

public abstract void pop(Appendable out, final int count) throws IOException;

public final CharSequence getI(Appendable out, final int indexFromTop) throws IOException {
return getT(out, indexFromTop, VarType.INTEGER);
}

public final CharSequence getL(Appendable out, final int indexFromTop) throws IOException {
return getT(out, indexFromTop, VarType.LONG);
}

public final CharSequence getF(Appendable out, final int indexFromTop) throws IOException {
return getT(out, indexFromTop, VarType.FLOAT);
}

public final CharSequence getD(Appendable out, final int indexFromTop) throws IOException {
return getT(out, indexFromTop, VarType.DOUBLE);
}

public final CharSequence getA(Appendable out, final int indexFromTop) throws IOException {
return getT(out, indexFromTop, VarType.REFERENCE);
}

public final CharSequence getT(Appendable out, final int indexFromTop, final int type) throws IOException {
return getT(out, indexFromTop, type, true);
}
public abstract CharSequence getT(Appendable out, final int indexFromTop, final int type, boolean clear) throws IOException;

public abstract Variable get(Appendable out, final int indexFromTop) throws IOException;

public static Variable getVariable(final int typeAndIndex) {
final int type = typeAndIndex & 0xff;
final int index = typeAndIndex >> 8;

return Variable.getStackVariable(type, index);
}

abstract boolean alwaysUseGt();

abstract int initCode(Appendable out) throws IOException;

abstract void finishStatement(Appendable out) throws IOException;

abstract void caughtException(Appendable out, String e) throws IOException;
}
@@ -406,6 +406,13 @@ private String compileImpl(Appendable out, final String cn) throws IOException {
return "";
}

private boolean versionCheck() throws IOException {
if (jc.getMajor_version() < 50) {
return false;
}
return true;
}

private StringArray findJavaScriptResources(byte[] arr, final String cn) throws IOException {
if (arr == null) {
return null;
@@ -498,25 +505,18 @@ private boolean generateMethod(Appendable out, String destObject, String name, M
out.append(") {").append("\n");

final byte[] byteCodes = m.getCode();
if (byteCodes == null || jc.getMajor_version() < 50) {
if (byteCodes == null) {
byte[] defaultAttr = m.getDefaultAttribute();
if (defaultAttr != null) {
out.append(" return ");
AnnotationParser ap = new GenerateAnno(out, true, false);
ap.parseDefault(defaultAttr, jc);
out.append(";\n");
} else {
if (debug(out, " throw 'no code found for ")) {
out.append(jc.getClassName()).append('.')
.append(m.getName()).append("';\n");
}
}
if (byteCodes == null) {
byte[] defaultAttr = m.getDefaultAttribute();
if (defaultAttr != null) {
out.append(" return ");
AnnotationParser ap = new GenerateAnno(out, true, false);
ap.parseDefault(defaultAttr, jc);
out.append(";\n");
} else {
out.append(" throw 'Class file version for " + jc.getClassName() + " is " + jc.getMajor_version() + "."
+ jc.getMinor_version() + " - recompile with -target 1.6 (at least)';\n"
);

if (debug(out, " throw 'no code found for ")) {
out.append(jc.getClassName()).append('.')
.append(m.getName()).append("';\n");
}
}
if (defineProp) {
out.append("}});");
@@ -526,7 +526,12 @@ private boolean generateMethod(Appendable out, String destObject, String name, M
return defineProp;
}

final StackMapper smapper = new StackMapper();
final AbstractStackMapper smapper;
if (versionCheck()) {
smapper = new StackMapper();
} else {
smapper = new OldStackMapper();
}

if (!m.isStatic()) {
out.append(" var ").append(" lcA0 = this;\n");
@@ -550,7 +555,7 @@ private boolean generateMethod(Appendable out, String destObject, String name, M
return defineProp;
}

static int generateIf(Appendable out, StackMapper mapper, byte[] byteCodes,
static int generateIf(Appendable out, AbstractStackMapper mapper, byte[] byteCodes,
int i, final CharSequence v2, final CharSequence v1,
final String test, int topMostLabel
) throws IOException {
@@ -559,7 +564,7 @@ static int generateIf(Appendable out, StackMapper mapper, byte[] byteCodes,
out.append("if ((").append(v1)
.append(") ").append(test).append(" (")
.append(v2).append(")) ");
goTo(out, i, indx, topMostLabel);
goTo(out, i, indx, topMostLabel, mapper.alwaysUseGt());
return i + 2;
}

@@ -1108,13 +1113,13 @@ private static String outputArg(Appendable out, String[] args, int indx) throws
}

final void emitNoFlush(
Appendable out, StackMapper sm,
Appendable out, AbstractStackMapper sm,
final String format, final CharSequence... params
) throws IOException {
emitImpl(out, format, params);
}
static final void emit(
final Appendable out, StackMapper sm, final String format, final CharSequence... params
final Appendable out, AbstractStackMapper sm, final String format, final CharSequence... params
) throws IOException {
sm.flush(out);
emitImpl(out, format, params);
@@ -1144,8 +1149,9 @@ static void emitImpl(final Appendable out,
out.append(format, processed, length);
}

void generateCatch(Appendable out, TrapData[] traps, int current, int topMostLabel) throws IOException {
void generateCatch(Appendable out, AbstractStackMapper mapper, TrapData[] traps, int current, int topMostLabel, boolean useGt) throws IOException {
out.append("} catch (e) {\n");
out.append(" e = vm.java_lang_Class(false).bck2BrwsrThrwrbl(e);\n");
int finallyPC = -1;
for (TrapData e : traps) {
if (e == null) {
@@ -1154,25 +1160,29 @@ void generateCatch(Appendable out, TrapData[] traps, int current, int topMostLab
if (e.catch_cpx != 0) { //not finally
final String classInternalName = jc.getClassName(e.catch_cpx);
addReference(out, classInternalName);
out.append("e = vm.java_lang_Class(false).bck2BrwsrThrwrbl(e);");
out.append("if (e['$instOf_" + mangleClassName(classInternalName) + "']) {");
out.append("var stA0 = e;");
goTo(out, current, e.handler_pc, topMostLabel);
out.append("}\n");
out.append(" if (e['$instOf_" + mangleClassName(classInternalName) + "']) {\n");
mapper.caughtException(out, "e");
goTo(out, current, e.handler_pc, topMostLabel, useGt);
out.append(" }\n");
} else {
finallyPC = e.handler_pc;
}
}
if (finallyPC == -1) {
out.append("throw e;");
} else {
out.append("var stA0 = e;");
goTo(out, current, finallyPC, topMostLabel);
mapper.caughtException(out, "e");
goTo(out, current, finallyPC, topMostLabel, useGt);
}
out.append("\n}");
}

static void goTo(Appendable out, int current, int to, int canBack) throws IOException {
static void goTo(Appendable out, int current, int to, int canBack, boolean useGt) throws IOException {
if (useGt) {
out.append("{ gt = " + to + "; continue X_0; }");
return;
}

if (to < current) {
if (canBack < to) {
out.append("{ gt = 0; continue X_" + to + "; }");
@@ -1185,14 +1195,14 @@ static void goTo(Appendable out, int current, int to, int canBack) throws IOExce
}

static void emitIf(
Appendable out, StackMapper sm, String pattern, CharSequence param, int current, int to, int canBack
Appendable out, AbstractStackMapper sm, String pattern, CharSequence param, int current, int to, int canBack
) throws IOException {
sm.flush(out);
emitImpl(out, pattern, param);
goTo(out, current, to, canBack);
goTo(out, current, to, canBack, sm.alwaysUseGt());
}

void generateNewArray(Appendable out, int atype, final StackMapper smapper) throws IOException, IllegalStateException {
void generateNewArray(Appendable out, int atype, final AbstractStackMapper smapper) throws IOException, IllegalStateException {
String jvmType;
switch (atype) {
case 4: jvmType = "[Z"; break;
@@ -1207,10 +1217,10 @@ void generateNewArray(Appendable out, int atype, final StackMapper smapper) thro
}
emit(out, smapper,
"var @2 = Array.prototype['newArray__Ljava_lang_Object_2ZLjava_lang_String_2Ljava_lang_Object_2I'](true, '@3', null, @1);",
smapper.popI(), smapper.pushA(), jvmType);
smapper.popI(out), smapper.pushA(), jvmType);
}

void generateANewArray(Appendable out, int type, final StackMapper smapper) throws IOException {
void generateANewArray(Appendable out, int type, final AbstractStackMapper smapper) throws IOException {
String typeName = jc.getClassName(type);
String ref = "null";
if (typeName.startsWith("[")) {
@@ -1221,10 +1231,10 @@ void generateANewArray(Appendable out, int type, final StackMapper smapper) thro
}
emit(out, smapper,
"var @2 = Array.prototype['newArray__Ljava_lang_Object_2ZLjava_lang_String_2Ljava_lang_Object_2I'](false, @3, @4, @1);",
smapper.popI(), smapper.pushA(), typeName, ref);
smapper.popI(out), smapper.pushA(), typeName, ref);
}

int generateMultiANewArray(Appendable out, int type, final byte[] byteCodes, int i, final StackMapper smapper) throws IOException {
int generateMultiANewArray(Appendable out, int type, final byte[] byteCodes, int i, final AbstractStackMapper smapper) throws IOException {
String typeName = jc.getClassName(type);
int dim = readUByte(byteCodes, ++i);
StringBuilder dims = new StringBuilder();
@@ -1233,7 +1243,7 @@ int generateMultiANewArray(Appendable out, int type, final byte[] byteCodes, int
if (d != 0) {
dims.insert(1, ",");
}
dims.insert(1, smapper.popI());
dims.insert(1, smapper.popI(out));
}
dims.append(']');
String fn = "null";
@@ -1247,59 +1257,59 @@ int generateMultiANewArray(Appendable out, int type, final byte[] byteCodes, int
return i;
}

int generateTableSwitch(Appendable out, int i, final byte[] byteCodes, final StackMapper smapper, int topMostLabel) throws IOException {
int generateTableSwitch(Appendable out, int i, final byte[] byteCodes, final AbstractStackMapper smapper, int topMostLabel) throws IOException {
int table = i / 4 * 4 + 4;
int dflt = i + readInt4(byteCodes, table);
table += 4;
int low = readInt4(byteCodes, table);
table += 4;
int high = readInt4(byteCodes, table);
table += 4;
final CharSequence swVar = smapper.popValue();
final CharSequence swVar = smapper.popValue(out);
smapper.flush(out);
out.append("switch (").append(swVar).append(") {\n");
while (low <= high) {
int offset = i + readInt4(byteCodes, table);
table += 4;
out.append(" case " + low).append(":"); goTo(out, i, offset, topMostLabel); out.append('\n');
out.append(" case " + low).append(":"); goTo(out, i, offset, topMostLabel, smapper.alwaysUseGt()); out.append('\n');
low++;
}
out.append(" default: ");
goTo(out, i, dflt, topMostLabel);
goTo(out, i, dflt, topMostLabel, smapper.alwaysUseGt());
out.append("\n}");
i = table - 1;
return i;
}

int generateLookupSwitch(Appendable out, int i, final byte[] byteCodes, final StackMapper smapper, int topMostLabel) throws IOException {
int generateLookupSwitch(Appendable out, int i, final byte[] byteCodes, final AbstractStackMapper smapper, int topMostLabel) throws IOException {
int table = i / 4 * 4 + 4;
int dflt = i + readInt4(byteCodes, table);
table += 4;
int n = readInt4(byteCodes, table);
table += 4;
final CharSequence swVar = smapper.popValue();
final CharSequence swVar = smapper.popValue(out);
smapper.flush(out);
out.append("switch (").append(swVar).append(") {\n");
while (n-- > 0) {
int cnstnt = readInt4(byteCodes, table);
table += 4;
int offset = i + readInt4(byteCodes, table);
table += 4;
out.append(" case " + cnstnt).append(": "); goTo(out, i, offset, topMostLabel); out.append('\n');
out.append(" case " + cnstnt).append(": "); goTo(out, i, offset, topMostLabel, smapper.alwaysUseGt()); out.append('\n');
}
out.append(" default: ");
goTo(out, i, dflt, topMostLabel);
goTo(out, i, dflt, topMostLabel, smapper.alwaysUseGt());
out.append("\n}");
i = table - 1;
return i;
}

void generateInstanceOf(Appendable out, int indx, final StackMapper smapper) throws IOException {
void generateInstanceOf(Appendable out, int indx, final AbstractStackMapper smapper) throws IOException {
String type = jc.getClassName(indx);
if (!type.startsWith("[")) {
emit(out, smapper,
"var @2 = @1 != null && @1['$instOf_@3'] ? 1 : 0;",
smapper.popA(), smapper.pushI(),
smapper.popA(out), smapper.pushI(),
mangleClassName(type));
} else {
int cnt = 0;
@@ -1312,24 +1322,24 @@ void generateInstanceOf(Appendable out, int indx, final StackMapper smapper) thr
type = "vm." + mangleClassName(component);
emit(out, smapper,
"var @2 = Array.prototype['isInstance__ZLjava_lang_Object_2ILjava_lang_Object_2'](@1, @4, @3);",
smapper.popA(), smapper.pushI(),
smapper.popA(out), smapper.pushI(),
type, "" + cnt
);
} else {
emit(out, smapper,
"var @2 = Array.prototype['isInstance__ZLjava_lang_Object_2Ljava_lang_String_2'](@1, '@3');",
smapper.popA(), smapper.pushI(), type
smapper.popA(out), smapper.pushI(), type
);
}
}
}

void generateCheckcast(Appendable out, int indx, final StackMapper smapper) throws IOException {
void generateCheckcast(Appendable out, int indx, final AbstractStackMapper smapper) throws IOException {
String type = jc.getClassName(indx);
if (!type.startsWith("[")) {
emitNoFlush(out, smapper,
"if (@1 !== null && !@1['$instOf_@2']) vm.java_lang_Class(false).castEx(@1, '@3');",
smapper.getT(0, VarType.REFERENCE, false), mangleClassName(type), type.replace('/', '.'));
smapper.getT(out, 0, VarType.REFERENCE, false), mangleClassName(type), type.replace('/', '.'));
} else {
int cnt = 0;
while (type.charAt(cnt) == '[') {
@@ -1341,12 +1351,12 @@ void generateCheckcast(Appendable out, int indx, final StackMapper smapper) thro
type = "vm." + mangleClassName(component);
emitNoFlush(out, smapper,
"if (@1 !== null && !Array.prototype['isInstance__ZLjava_lang_Object_2ILjava_lang_Object_2'](@1, @3, @2)) vm.java_lang_Class(false).castEx(@1, '');",
smapper.getT(0, VarType.REFERENCE, false), type, "" + cnt
smapper.getT(out, 0, VarType.REFERENCE, false), type, "" + cnt
);
} else {
emitNoFlush(out, smapper,
"if (@1 !== null && !Array.prototype['isInstance__ZLjava_lang_Object_2Ljava_lang_String_2'](@1, '@2')) vm.java_lang_Class(false).castEx(@1, '');",
smapper.getT(0, VarType.REFERENCE, false), type
smapper.getT(out, 0, VarType.REFERENCE, false), type
);
}
}

Large diffs are not rendered by default.

@@ -32,12 +32,12 @@ abstract class IndyHandler {

static class Ctx {
final Appendable out;
final StackMapper stackMapper;
final AbstractStackMapper stackMapper;
final ByteCodeToJavaScript byteCodeToJavaScript;
final ByteCodeParser.BootMethodData bm;
final String[] mt;

Ctx(Appendable out, StackMapper m, ByteCodeToJavaScript bc, String[] methodAndType, ByteCodeParser.BootMethodData bm) {
Ctx(Appendable out, AbstractStackMapper m, ByteCodeToJavaScript bc, String[] methodAndType, ByteCodeParser.BootMethodData bm) {
this.out = out;
this.stackMapper = m;
this.byteCodeToJavaScript = bc;

Large diffs are not rendered by default.

@@ -78,14 +78,14 @@ protected boolean handle(Ctx ctx) throws IOException {
fixedArgsCount = cnt.length();
final CharSequence[] vars = new CharSequence[fixedArgsCount];
for (int j = fixedArgsCount - 1; j >= 0; --j) {
vars[j] = ctx.stackMapper.popValue();
vars[j] = ctx.stackMapper.popValue(ctx.out);
}

assert returnType[0] == 'L';

ctx.stackMapper.flush(ctx.out);

final Variable samVar = ctx.stackMapper.pushA();
final CharSequence samVar = ctx.stackMapper.pushA();
ctx.out.append("var ").append(samVar).append(" = ").append(interfaceToCreate).append(".constructor.$class.$lambda([");

String sep = "";
@@ -0,0 +1,132 @@
/**
* Back 2 Browser Bytecode Translator
* Copyright (C) 2012-2018 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://opensource.org/licenses/GPL-2.0.
*/
package org.apidesign.vm4brwsr;

import java.io.IOException;

final class OldStackMapper extends AbstractStackMapper {
int counter;
int flushed;

OldStackMapper() {
}

@Override
public void clear() {
}

@Override
public void syncWithFrameStack(ByteCodeParser.TypeArray frameStack) {
}

@Override
int initCode(Appendable out) throws IOException {
out.append(" var gt = 0;\n");
out.append(" var stack = [];\n");
out.append(" X_0: for (;;) {\n");
return 1;
}

@Override
public CharSequence pushT(int type) {
return Variable.getStackVariable(VarType.REFERENCE, ++counter);
}

@Override
void assign(Appendable out, int varType, CharSequence s) throws IOException {
out.append("stack.push(").append(s).append(");");
}

@Override
void replace(Appendable out, int varType, String format, CharSequence... arr) throws IOException {
out.append("stack[stack.length - 1] = ");
ByteCodeToJavaScript.emitImpl(out, format, arr);
out.append(";\n");
}

@Override
void flush(Appendable out) throws IOException {
}

@Override
void finishStatement(Appendable out) throws IOException {
while (flushed < counter) {
out.append("stack.push(").append(Variable.getStackVariable(VarType.REFERENCE, ++flushed)).append(");\n");
}
out.append("}\n");
}

@Override
public boolean isDirty() {
return false;
}

@Override
public CharSequence popT(Appendable out, int type) throws IOException {
Variable v = Variable.getStackVariable(VarType.REFERENCE, ++counter);
out.append("var ").append(v).append(" = stack.pop();\n");
flushed = counter;
return v;
}

@Override
public CharSequence popValue(Appendable out) throws IOException {
return pop(out);
}

@Override
public Variable pop(Appendable out) throws IOException {
Variable v = Variable.getStackVariable(VarType.REFERENCE, ++counter);
out.append("var ").append(v).append(" = stack.pop();\n");
flushed = counter;
return v;
}

@Override
public void pop(Appendable out, int count) throws IOException {
while (count-- > 0) {
out.append("stack.pop();");
}
}

@Override
public CharSequence getT(Appendable out, int indexFromTop, int type, boolean clear) throws IOException {
Variable v = Variable.getStackVariable(VarType.REFERENCE, ++counter);
out.append("var ").append(v).append(" = stack[stack.length - " + (indexFromTop + 1) + "];\n");
flushed = counter;
return v;
}

@Override
public Variable get(Appendable out, int indexFromTop) throws IOException {
Variable v = Variable.getStackVariable(VarType.REFERENCE, ++counter);
out.append("var ").append(v).append(" = stack[stack.length - " + (indexFromTop + 1) + "];\n");
flushed = counter;
return v;
}

@Override
boolean alwaysUseGt() {
return true;
}

@Override
void caughtException(Appendable out, String e) throws IOException {
out.append("stack = [").append(e).append("];");
}
}
@@ -20,7 +20,7 @@
import java.io.IOException;
import org.apidesign.vm4brwsr.ByteCodeParser.TypeArray;

final class StackMapper {
final class StackMapper extends AbstractStackMapper {
private final TypeArray stackTypeIndexPairs;
private final StringArray stackValues;
private boolean dirty;
@@ -45,34 +45,17 @@ public void syncWithFrameStack(final TypeArray frameStack) {
}
}

public Variable pushI() {
return pushT(VarType.INTEGER);
}

public Variable pushL() {
return pushT(VarType.LONG);
}

public Variable pushF() {
return pushT(VarType.FLOAT);
}

public Variable pushD() {
return pushT(VarType.DOUBLE);
}

public Variable pushA() {
return pushT(VarType.REFERENCE);
}

@Override
public Variable pushT(final int type) {
return getVariable(pushTypeImpl(type));
}

@Override
void assign(Appendable out, int varType, CharSequence s) throws IOException {
pushTypeAndValue(varType, s);
}

@Override
void replace(Appendable out, int varType, String format, CharSequence... arr)
throws IOException {
StringBuilder sb = new StringBuilder();
@@ -84,7 +67,12 @@ void replace(Appendable out, int varType, String format, CharSequence... arr)
final int value = (last << 8) | (varType & 0xff);
stackTypeIndexPairs.set(last, value);
}

@Override
void finishStatement(Appendable out) throws IOException {
}

@Override
void flush(Appendable out) throws IOException {
int count = stackTypeIndexPairs.getSize();
for (int i = 0; i < count; i++) {
@@ -98,80 +86,43 @@ void flush(Appendable out) throws IOException {
dirty = false;
}

@Override
public boolean isDirty() {
return dirty;
}

public CharSequence popI() {
return popT(VarType.INTEGER);
}

public CharSequence popL() {
return popT(VarType.LONG);
}

public CharSequence popF() {
return popT(VarType.FLOAT);
}

public CharSequence popD() {
return popT(VarType.DOUBLE);
}

public CharSequence popA() {
return popT(VarType.REFERENCE);
}

public CharSequence popT(final int type) {
final CharSequence variable = getT(0, type);
@Override
public CharSequence popT(Appendable out, final int type) throws IOException {
final CharSequence variable = getT(out, 0, type);
popImpl(1);
return variable;
}

public CharSequence popValue() {
final CharSequence variable = getT(0, -1);
@Override
public CharSequence popValue(Appendable out) throws IOException {
final CharSequence variable = getT(out, 0, -1);
popImpl(1);
return variable;
}
@Override
public Variable pop(Appendable out) throws IOException {
flush(out);
final Variable variable = get(0);
final Variable variable = get(out, 0);
popImpl(1);
return variable;
}

public void pop(final int count) {
@Override
public void pop(Appendable out, final int count) {
final int stackSize = stackTypeIndexPairs.getSize();
if (count > stackSize) {
throw new IllegalStateException("Stack underflow");
}
popImpl(count);
}

public CharSequence getI(final int indexFromTop) {
return getT(indexFromTop, VarType.INTEGER);
}

public CharSequence getL(final int indexFromTop) {
return getT(indexFromTop, VarType.LONG);
}

public CharSequence getF(final int indexFromTop) {
return getT(indexFromTop, VarType.FLOAT);
}

public CharSequence getD(final int indexFromTop) {
return getT(indexFromTop, VarType.DOUBLE);
}

public CharSequence getA(final int indexFromTop) {
return getT(indexFromTop, VarType.REFERENCE);
}

public CharSequence getT(final int indexFromTop, final int type) {
return getT(indexFromTop, type, true);
}
public CharSequence getT(final int indexFromTop, final int type, boolean clear) {
@Override
public CharSequence getT(Appendable out, final int indexFromTop, final int type, boolean clear) {
final int stackSize = stackTypeIndexPairs.getSize();
if (indexFromTop >= stackSize) {
throw new IllegalStateException("Stack underflow");
@@ -189,7 +140,8 @@ public CharSequence getT(final int indexFromTop, final int type, boolean clear)
return getVariable(stackValue);
}

public Variable get(final int indexFromTop) {
@Override
public Variable get(Appendable out, final int indexFromTop) {
final int stackSize = stackTypeIndexPairs.getSize();
if (indexFromTop >= stackSize) {
throw new IllegalStateException("Stack underflow");
@@ -232,10 +184,18 @@ private void popImpl(final int count) {
stackTypeIndexPairs.setSize(stackSize - count);
}

public Variable getVariable(final int typeAndIndex) {
final int type = typeAndIndex & 0xff;
final int index = typeAndIndex >> 8;
@Override
boolean alwaysUseGt() {
return false;
}

@Override
int initCode(Appendable out) throws IOException {
return 0;
}

return Variable.getStackVariable(type, index);
@Override
void caughtException(Appendable out, String e) throws IOException {
out.append("var stA0 = ").append(e).append(";");
}
}
@@ -38,13 +38,13 @@ protected boolean handle(Ctx ctx) throws IOException {
fixedArgsCount = cnt.length();
final CharSequence[] vars = new CharSequence[fixedArgsCount];
for (int j = fixedArgsCount - 1; j >= 0; --j) {
vars[j] = ctx.stackMapper.popValue();
vars[j] = ctx.stackMapper.popValue(ctx.out);
}


ctx.stackMapper.flush(ctx.out);

final Variable stringVar = ctx.stackMapper.pushA();
final CharSequence stringVar = ctx.stackMapper.pushA();
ctx.out.append("\nvar ").append(stringVar).append(" = ");

StringBuilder sep = new StringBuilder();
@@ -224,7 +224,12 @@ private void generateBody(Appendable out, StringArray names) throws IOException
if (name == null) {
break;
}
InputStream is = resources.get(name + ".class");
InputStream is;
try {
is = resources.get(name + ".class");
} catch (IOException noSuchResource) {
is = null;
}
if (is == null) {
lazyReference(out, name);
skipClass.add(name);
@@ -235,7 +240,7 @@ private void generateBody(Appendable out, StringArray names) throws IOException
processed.add(name);
initCode.add(ic == null ? "" : ic);
} catch (RuntimeException ex) {
throw new IOException("Error while compiling " + name + "\n", ex);
throw new IOException("Error while compiling " + name + "\n" + ex.getClass().getName() + ": " + ex.getMessage(), ex);
}
}

@@ -46,7 +46,8 @@ static byte[] readClass(String name) throws IOException {
if (u == null) {
throw new IOException("Can't find " + name);
}
try (InputStream is = u.openStream()) {
InputStream is = u.openStream();
try {
byte[] arr;
arr = new byte[is.available()];
int offset = 0;
@@ -58,6 +59,8 @@ static byte[] readClass(String name) throws IOException {
offset += len;
}
return arr;
} finally {
is.close();
}
}

@@ -281,7 +281,7 @@ public static Object reflectiveMethodCall(boolean direct, String mn) throws Exce

public static int reflectiveSum(int a, int b) throws Exception {
Method m = StaticMethod.class.getMethod("sum", int.class, int.class);
return (int) m.invoke(null, a, b);
return ((Number) m.invoke(null, a, b)).intValue();
}

private abstract class Application {
@@ -71,7 +71,9 @@ public static String newInstance(String n) {
return ("CNFE:" + ex.getMessage()).toString();
}
return c.newInstance().getClass().getName();
} catch (InstantiationException | IllegalAccessException ex) {
} catch (InstantiationException ex) {
return ex.getMessage();
} catch (IllegalAccessException ex) {
return ex.getMessage();
}
}
@@ -26,6 +26,7 @@
import java.io.IOException;
import java.io.InputStream;
import static org.testng.Assert.*;
import org.testng.SkipException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -37,7 +38,8 @@
public class SizeOfAMethodTest {
private static String code;

@Test public void sumXYShouldBeSmall() {
@Test
public void sumXYShouldBeSmall() {
String s = code;
int beg = s.indexOf("c.sum__III");
int end = s.indexOf(".access", beg);
@@ -46,7 +48,8 @@ public class SizeOfAMethodTest {
assertTrue(beg < end, "Found end of sum method in " + code);

String method = s.substring(beg, end);


assumeNewStackMapper(method);

assertEquals(method.indexOf("st"), -1, "There should be no stack operations:\n" + method);
}
@@ -61,10 +64,13 @@ public class SizeOfAMethodTest {

String method = s.substring(beg, end);

assumeNewStackMapper(method);

assertEquals(method.indexOf("stA1"), -1, "No need for stA1 register:\n" + method);
}

@Test public void deepConstructor() {
@Test
public void deepConstructor() {
String s = code;
int beg = s.indexOf("c.intHolder__I");
int end = s.indexOf(".access", beg);
@@ -73,11 +79,14 @@ public class SizeOfAMethodTest {
assertTrue(beg < end, "Found end of intHolder method in " + code);

String method = s.substring(beg, end);

assumeNewStackMapper(method);

assertEquals(method.indexOf("stA3"), -1, "No need for stA3 register on second constructor:\n" + method);
}

@Test public void emptyConstructorRequiresNoStack() {
@Test
public void emptyConstructorRequiresNoStack() {
String s = code;
int beg = s.indexOf("CLS.cons__V");
int end = s.indexOf(".access", beg);
@@ -87,12 +96,15 @@ public class SizeOfAMethodTest {

String method = s.substring(beg, end);
method = method.replace("constructor", "CNSTR");

assumeNewStackMapper(method);

assertEquals(method.indexOf("st"), -1, "There should be no stack operations:\n" + method);
assertEquals(method.indexOf("for"), -1, "There should be no for blocks:\n" + method);
}

@Test public void dontGeneratePrimitiveFinalConstants() {
@Test
public void dontGeneratePrimitiveFinalConstants() {
assertEquals(code.indexOf("MISSING_CONSTANT"), -1, "MISSING_CONSTANT field should not be generated");
}

@@ -117,5 +129,12 @@ public InputStream get(String resource) throws IOException {
public static void releaseTheCode() {
code = null;
}

private static void assumeNewStackMapper(String code) {
int stackArray = code.indexOf("var stack = [];");
if (stackArray >= 0) {
throw new SkipException("Skipping the text as code contains stack = []");
}
}

}
@@ -34,28 +34,28 @@ public StackMapperTest() {
throws Exception {
StringBuilder sb = new StringBuilder();

StackMapper smapper = new StackMapper();
AbstractStackMapper smapper = new StackMapper();

smapper.assign(sb, VarType.INTEGER, "0");

smapper.assign(sb, VarType.REFERENCE, "arr");
smapper.assign(sb, VarType.INTEGER, "33");
smapper.replace(sb, VarType.INTEGER, "@2[@1]",
smapper.popI(), smapper.getA(0)
smapper.popI(sb), smapper.getA(sb, 0)
);

smapper.replace(sb, VarType.INTEGER, "(@1) + (@2)",
smapper.getI(1), smapper.popI()
smapper.getI(sb, 1), smapper.popI(sb)
);

smapper.assign(sb, VarType.REFERENCE, "arr");
smapper.assign(sb, VarType.INTEGER, "22");
smapper.replace(sb, VarType.INTEGER, "@2[@1]",
smapper.popI(), smapper.getA(0)
smapper.popI(sb), smapper.getA(sb, 0)
);

smapper.replace(sb, VarType.INTEGER, "(@1) + (@2)",
smapper.getI(1), smapper.popI()
smapper.getI(sb, 1), smapper.popI(sb)
);

smapper.flush(sb);
@@ -104,12 +104,10 @@ public static int countAB(String txt) {
}

public static int stringSwitch(String txt) {
switch (txt) {
case "jedna": return 1;
case "dve": return 2;
case "tri": return 3;
case "ctyri": return 4;
}
if ("jedna".equals(txt)) return 1;
if ("dve".equals(txt)) return 2;
if ("tri".equals(txt)) return 3;
if ("ctyri".equals(txt)) return 4;
return -1;
}

@@ -85,13 +85,24 @@ public class StringTest {
);
}

@Test(timeOut=10000) public void toStringConcatenation() throws Exception {
@Test
public void toStringConcatenation1() throws Exception {
assertExec(
"Five executions should generate 5Hello World!",
StringSample.class, "toStringTest__Ljava_lang_String_2I",
"Hello World!1", 1
);
}

@Test
public void toStringConcatenation5() throws Exception {
assertExec(
"Five executions should generate 5Hello World!",
StringSample.class, "toStringTest__Ljava_lang_String_2I",
"Hello World!5", 5
);
}

@Test public void toStringConcatenationJava() throws Exception {
assertEquals("Hello World!5", StringSample.toStringTest(5));
}