Skip to content

Commit

Permalink
#296 Test all variants of JEP 280 style string concatenations (#299)
Browse files Browse the repository at this point in the history
* #296 Test all variants of JEP 280 style string concatenations
  • Loading branch information
mirkosertic authored Dec 7, 2019
1 parent 283ffba commit 944a6d7
Show file tree
Hide file tree
Showing 12 changed files with 448 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package de.mirkosertic.bytecoder.classlib;

import de.mirkosertic.bytecoder.api.EmulatedByRuntime;
import de.mirkosertic.bytecoder.api.Export;

import java.lang.invoke.ConstantCallSite;
Expand Down Expand Up @@ -158,4 +159,40 @@ public static void setByteArrayEntry(final byte[] aArray, final int aIndex, fina
public static Locale defaultLocale() {
return new Locale("en", "US");
}

@EmulatedByRuntime
public static native boolean isChar(final MethodType aType, final int aParamIndex);

@EmulatedByRuntime
public static native boolean isFloat(final MethodType aType, final int aParamIndex);

@EmulatedByRuntime
public static native boolean isDouble(final MethodType aType, final int aParamIndex);

@EmulatedByRuntime
public static native boolean isBoolean(final MethodType aType, final int aParamIndex);

@EmulatedByRuntime
public static native boolean isInteger(final MethodType aType, final int aParamIndex);

@EmulatedByRuntime
public static native boolean isLong(final MethodType aType, final int aParamIndex);

@EmulatedByRuntime
public static native int reinterpretAsInt(final Object o);

@EmulatedByRuntime
public static native long reinterpretAsLong(final Object o);

@EmulatedByRuntime
public static native float reinterpretAsFloat(final Object o);

@EmulatedByRuntime
public static native double reinterpretAsDouble(final Object o);

@EmulatedByRuntime
public static native char reinterpretAsChar(final Object o);

@EmulatedByRuntime
public static native boolean reinterpretAsBoolean(final Object o);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,65 @@
*/
package de.mirkosertic.bytecoder.classlib.java.lang.invoke;

import de.mirkosertic.bytecoder.api.SubstitutesInClass;
import de.mirkosertic.bytecoder.classlib.VM;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

import de.mirkosertic.bytecoder.api.SubstitutesInClass;
import de.mirkosertic.bytecoder.classlib.VM;

@SubstitutesInClass(completeReplace = true)
public class TStringConcatFactory {

public static CallSite makeConcat(MethodHandles.Lookup aLookup, String aName, MethodType aConcatType) {
public static CallSite makeConcat(final MethodHandles.Lookup aLookup, final String aName, final MethodType aConcatType) {

return new VM.ImplementingCallsite(null) {
@Override
public Object invokeExact(Object... args) throws Throwable {
StringBuilder theResult = new StringBuilder();
public Object invokeExact(final Object... args) throws Throwable {
final StringBuilder theResult = new StringBuilder();
if (args != null) {
for (int i=0;i<args.length;i++) {
theResult.append(args[i]);
appendTo(theResult, args[i], aConcatType, i);
}
}
return theResult.toString();
}
};
}

public static CallSite makeConcatWithConstants(MethodHandles.Lookup aLookup, String aName, MethodType aConcatType, String aRecipe, Object... aConstants) {
private static void appendTo(final StringBuilder aTarget, final Object aValue, final MethodType aType, final int aIndex) {
if (VM.isInteger(aType, aIndex)) {
aTarget.append(VM.reinterpretAsInt(aValue));
} else if (VM.isLong(aType, aIndex)) {
aTarget.append(VM.reinterpretAsLong(aValue));
} else if (VM.isFloat(aType, aIndex)) {
aTarget.append(VM.reinterpretAsFloat(aValue));
} else if (VM.isDouble(aType, aIndex)) {
aTarget.append(VM.reinterpretAsDouble(aValue));
} else if (VM.isBoolean(aType, aIndex)) {
aTarget.append(VM.reinterpretAsBoolean(aValue));
} else if (VM.isChar(aType, aIndex)) {
aTarget.append(VM.reinterpretAsChar(aValue));
} else {
aTarget.append(aValue);
}
}

public static CallSite makeConcatWithConstants(final MethodHandles.Lookup aLookup, final String aName, final MethodType aConcatType, final String aRecipe, final Object... aConstants) {

return new VM.ImplementingCallsite(null) {
@Override
public Object invokeExact(Object... args) throws Throwable {
public Object invokeExact(final Object... args) throws Throwable {
int theConstIndex = 0;
int theDynIndex = 0;
StringBuilder theResult = new StringBuilder();
int totalIndex = 0;
final StringBuilder theResult = new StringBuilder();
for (int i=0;i<aRecipe.length();i++) {
char theChar = aRecipe.charAt(i);
final char theChar = aRecipe.charAt(i);
if (theChar == 1) {
theResult.append(args[theDynIndex++]);
appendTo(theResult, args[theDynIndex++], aConcatType, totalIndex++);
} else if (theChar == 2) {
theResult.append(aConstants[theConstIndex++]);
appendTo(theResult, aConstants[theConstIndex++], aConcatType, totalIndex++);
} else {
theResult.append(theChar);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import de.mirkosertic.bytecoder.ssa.MethodHandlesGeneratedLookupExpression;
import de.mirkosertic.bytecoder.ssa.MethodParameterValue;
import de.mirkosertic.bytecoder.ssa.MethodRefExpression;
import de.mirkosertic.bytecoder.ssa.MethodTypeArgumentCheckExpression;
import de.mirkosertic.bytecoder.ssa.MethodTypeExpression;
import de.mirkosertic.bytecoder.ssa.MinExpression;
import de.mirkosertic.bytecoder.ssa.NegatedExpression;
Expand All @@ -101,6 +102,7 @@
import de.mirkosertic.bytecoder.ssa.PutFieldExpression;
import de.mirkosertic.bytecoder.ssa.PutStaticExpression;
import de.mirkosertic.bytecoder.ssa.RegionNode;
import de.mirkosertic.bytecoder.ssa.ReinterpretAsNativeExpression;
import de.mirkosertic.bytecoder.ssa.ResolveCallsiteObjectExpression;
import de.mirkosertic.bytecoder.ssa.ReturnExpression;
import de.mirkosertic.bytecoder.ssa.ReturnValueExpression;
Expand Down Expand Up @@ -267,11 +269,34 @@ private void print(final Value aValue) {
print((IsNaNExpression) aValue);
} else if (aValue instanceof NewInstanceFromDefaultConstructorExpression) {
print((NewInstanceFromDefaultConstructorExpression) aValue);
} else if (aValue instanceof MethodTypeArgumentCheckExpression) {
print((MethodTypeArgumentCheckExpression) aValue);
} else if (aValue instanceof ReinterpretAsNativeExpression) {
print((ReinterpretAsNativeExpression) aValue);
} else {
throw new IllegalStateException("Not implemented : " + aValue);
}
}

private void print(final ReinterpretAsNativeExpression aExpression) {
final Value theValue = aExpression.incomingDataFlows().get(0);
print(theValue);
}

private void print(final MethodTypeArgumentCheckExpression aExpression) {
final Value theValue = aExpression.incomingDataFlows().get(0);
final Value theIndex = aExpression.incomingDataFlows().get(1);
final TypeRef.Native theExpectedType = aExpression.getExpectedType();
writer.text("(");
print(theValue);
writer.text(".arguments[");
print(theIndex);
writer.text("]");
writer.text("==='");
writer.text(theExpectedType.name());
writer.text("')");
}

private void print(final NewInstanceFromDefaultConstructorExpression aExpression) {
final Value theClass = aExpression.incomingDataFlows().get(0);
print(theClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ public WASMCompileResult generateCodeFor(
if (theProvidingClass.emulatedByRuntime()) {
return;
}
if (t.emulatedByRuntime()) {
return;
}

// Native methods are imported via annotation
if (!t.getAccessFlags().isNative() && theProvidingClass.isOpaqueType()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,6 @@
*/
package de.mirkosertic.bytecoder.backend.wasm;

import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.call;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.currentMemory;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.f32;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.getGlobal;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.getLocal;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.i32;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.select;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.teeLocal;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionReference;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionTableReference;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import de.mirkosertic.bytecoder.allocator.AbstractAllocator;
import de.mirkosertic.bytecoder.allocator.Register;
import de.mirkosertic.bytecoder.backend.CompileOptions;
Expand Down Expand Up @@ -115,6 +94,7 @@
import de.mirkosertic.bytecoder.ssa.MemorySizeExpression;
import de.mirkosertic.bytecoder.ssa.MethodHandlesGeneratedLookupExpression;
import de.mirkosertic.bytecoder.ssa.MethodRefExpression;
import de.mirkosertic.bytecoder.ssa.MethodTypeArgumentCheckExpression;
import de.mirkosertic.bytecoder.ssa.MethodTypeExpression;
import de.mirkosertic.bytecoder.ssa.MinExpression;
import de.mirkosertic.bytecoder.ssa.NegatedExpression;
Expand All @@ -130,6 +110,7 @@
import de.mirkosertic.bytecoder.ssa.PutFieldExpression;
import de.mirkosertic.bytecoder.ssa.PutStaticExpression;
import de.mirkosertic.bytecoder.ssa.RegionNode;
import de.mirkosertic.bytecoder.ssa.ReinterpretAsNativeExpression;
import de.mirkosertic.bytecoder.ssa.ResolveCallsiteObjectExpression;
import de.mirkosertic.bytecoder.ssa.ReturnExpression;
import de.mirkosertic.bytecoder.ssa.ReturnValueExpression;
Expand All @@ -151,6 +132,27 @@
import de.mirkosertic.bytecoder.ssa.VariableAssignmentExpression;
import de.mirkosertic.bytecoder.stackifier.Stackifier;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.call;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.currentMemory;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.f32;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.getGlobal;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.getLocal;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.i32;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.select;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.teeLocal;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionReference;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionTableReference;

public class WASMSSAASTWriter {

public static String registerName(final Register r) {
Expand Down Expand Up @@ -808,9 +810,80 @@ private WASMValue toValue(final Value aValue) {
if (aValue instanceof PtrOfExpression) {
return ptrOfExpression((PtrOfExpression) aValue);
}
if (aValue instanceof MethodTypeArgumentCheckExpression) {
return methodTypeArgumentCheckExpression((MethodTypeArgumentCheckExpression) aValue);
}
if (aValue instanceof ReinterpretAsNativeExpression) {
return reinterpretAsNativeExpression((ReinterpretAsNativeExpression) aValue);
}
throw new IllegalStateException("Not supported : " + aValue);
}

private WASMValue methodTypeValue(final MethodTypeExpression aValue) {
final BytecodeMethodSignature theSignature = aValue.getSignature();
final String theMethodTypeFactoryName = WASMWriterUtils.toMethodName("methodTypeFactory", theSignature);
ExportableFunction theFactoryFunction;
try {
theFactoryFunction = module.functionIndex().firstByLabel(theMethodTypeFactoryName);
} catch (final Exception e) {
theFactoryFunction = module.getFunctions().newFunction(theMethodTypeFactoryName, PrimitiveType.i32);
final Local data = theFactoryFunction.newLocal("data", PrimitiveType.i32);
final int length = 1 + theSignature.getArguments().length;
theFactoryFunction.flow.setLocal(data, newArray(i32.c(length, null)), null);

final Expressions f = theFactoryFunction.flow;
final java.util.function.BiFunction<BytecodeTypeRef, Integer, Void> theAdder = (aType, aIndex) -> {
final int offset = 20 + aIndex * 4;
if (aType.isPrimitive()) {
final TypeRef.Native theNativeType = (TypeRef.Native) TypeRef.toType(aType);
// Negative number to indicate it is a primitive type
f.i32.store(offset, getLocal(data, null), i32.c(-theNativeType.ordinal(), null), aValue);
} else {
// Positive number with the id of the class
if (aType.isArray()) {
final BytecodeLinkedClass theLinkedClass = linkerContext.resolveClass(BytecodeObjectTypeRef.fromRuntimeClass(Array.class));
f.i32.store(offset, getLocal(data, null), i32.c(theLinkedClass.getUniqueId(), null), aValue);
} else {
final BytecodeLinkedClass theLinkedClass = linkerContext.resolveClass((BytecodeObjectTypeRef) aType);
f.i32.store(offset, getLocal(data, null), i32.c(theLinkedClass.getUniqueId(), null), aValue);
}
}
return null;
};
// Return type and arguments
theAdder.apply(theSignature.getReturnType(), 0);
for (int i=0;i<theSignature.getArguments().length;i++) {
final BytecodeTypeRef theArgument = theSignature.getArguments()[i];
theAdder.apply(theArgument, i + 1);
}

theFactoryFunction.flow.ret(getLocal(data, null), null);
}
return call(theFactoryFunction, Collections.emptyList(), aValue);
}

private WASMValue methodTypeArgumentCheckExpression(final MethodTypeArgumentCheckExpression aExpression) {
final TypeRef.Native theExpectedType = aExpression.getExpectedType();
final Value theMethodType = aExpression.incomingDataFlows().get(0);
final Value theIndex = aExpression.incomingDataFlows().get(1);

final WASMValue thePtr = i32.add(toValue(theMethodType), i32.mul(toValue(theIndex), i32.c(4, aExpression), aExpression), aExpression);
final WASMValue theExpectedValue = i32.c(- theExpectedType.ordinal(), null);
final WASMValue theRead = i32.load(20, thePtr, aExpression);
return i32.eq(theExpectedValue, theRead, aExpression);
}

private WASMValue reinterpretAsNativeExpression(final ReinterpretAsNativeExpression aExpression) {
final Value theValue = aExpression.incomingDataFlows().get(0);
switch (aExpression.getExpectedType()) {
case FLOAT:
case DOUBLE:
return f32.convert_sI32(toValue(theValue), aExpression);
default:
return toValue(theValue);
}
}

private WASMValue ptrOfExpression(final PtrOfExpression aValue) {
return toValue(aValue.incomingDataFlows().get(0));
}
Expand Down Expand Up @@ -951,13 +1024,6 @@ private WASMValue currentException(final CurrentExceptionExpression aValue) {
return i32.c(0, aValue);
}

private WASMValue methodTypeValue(final MethodTypeExpression aValue) {
// print("(i32.const ");
// print(idResolver.resolveTypeIDForSignature(aValue.getSignature()));
// print(")");
return i32.c(0, aValue);
}

private WASMValue methodHandlesGeneratedLookupValue(final MethodHandlesGeneratedLookupExpression aValue) {
return i32.c(0, aValue);
}
Expand Down Expand Up @@ -1038,7 +1104,7 @@ private WASMValue stringValue(final StringValue aValue) {
return getGlobal(resolver.globalForStringFromPool(aValue), null);
}

private WASMExpression newArray(final Value aValue) {
private WASMExpression newArray(final WASMValue aValue) {
final String theMethodName = WASMWriterUtils.toMethodName(
BytecodeObjectTypeRef.fromRuntimeClass(MemoryManager.class),
"newArray",
Expand All @@ -1048,7 +1114,11 @@ private WASMExpression newArray(final Value aValue) {
final WeakFunctionReferenceCallable theClassInit = weakFunctionReference(theClassName + CLASSINITSUFFIX, null);
final Function theFunction = module.functionIndex().firstByLabel(theMethodName);

return call(theFunction, Arrays.asList(i32.c(0, null), toValue(aValue), call(theClassInit, Collections.emptyList(), null), weakFunctionTableReference(theClassName + VTABLEFUNCTIONSUFFIX, null)), null);
return call(theFunction, Arrays.asList(i32.c(0, null), aValue, call(theClassInit, Collections.emptyList(), null), weakFunctionTableReference(theClassName + VTABLEFUNCTIONSUFFIX, null)), null);
}

private WASMExpression newArray(final Value aValue) {
return newArray(toValue(aValue));
}

private WASMValue newArrayValue(final NewArrayExpression aValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package de.mirkosertic.bytecoder.core;

import de.mirkosertic.bytecoder.api.DelegatesTo;
import de.mirkosertic.bytecoder.api.EmulatedByRuntime;
import de.mirkosertic.bytecoder.graph.EdgeType;
import de.mirkosertic.bytecoder.graph.Node;

Expand Down Expand Up @@ -80,6 +81,10 @@ public <T extends BytecodeAttributeInfo> T attributeByType(final Class<T> aAttri
return null;
}

public boolean emulatedByRuntime() {
return getAttributes().getAnnotationByType(EmulatedByRuntime.class.getName()) != null;
}

public BytecodeMethodSignature getSignature() {
return signature;
}
Expand Down
Loading

0 comments on commit 944a6d7

Please sign in to comment.