Skip to content

Commit

Permalink
#290 OpaqueTypes error (#298)
Browse files Browse the repository at this point in the history
  • Loading branch information
mirkosertic authored Dec 2, 2019
1 parent aed9529 commit 8e93406
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import de.mirkosertic.bytecoder.core.BytecodeResolvedMethods;
import de.mirkosertic.bytecoder.core.BytecodeTypeRef;
import de.mirkosertic.bytecoder.core.BytecodeUtf8Constant;
import de.mirkosertic.bytecoder.core.BytecodeVirtualMethodIdentifier;
import de.mirkosertic.bytecoder.relooper.Relooper;
import de.mirkosertic.bytecoder.ssa.ArrayEntryExpression;
import de.mirkosertic.bytecoder.ssa.ArrayLengthExpression;
Expand Down Expand Up @@ -1040,22 +1039,27 @@ private void print(final InvokeVirtualMethodExpression aValue) {
final Value theTarget = theIncomingData.get(0);
final List<Value> theArguments = theIncomingData.subList(1, theIncomingData.size());

// Check if we are invoking something on an opaque type
final BytecodeVirtualMethodIdentifier theMethodIdentifier = linkerContext.getMethodCollection().identifierFor(theMethodName, theSignature);
final List<BytecodeLinkedClass> theClasses = linkerContext.getAllClassesAndInterfacesWithMethod(theMethodIdentifier);
if (theClasses.size() == 1) {
final BytecodeLinkedClass theTargetClass = theClasses.get(0);
final BytecodeMethod theMethod = theTargetClass.getBytecodeClass().methodByNameAndSignatureOrNull(theMethodName, theSignature);
if (theTargetClass.isOpaqueType() && !theMethod.isConstructor()) {
writeOpaqueMethodInvocation(theSignature, theTarget, theArguments, theMethod);
return;
final BytecodeTypeRef theInvokedClassName = aValue.getInvokedClass();
if (!theInvokedClassName.isPrimitive() && !theInvokedClassName.isArray()) {
final BytecodeLinkedClass theInvokedClass = linkerContext.resolveClass((BytecodeObjectTypeRef) theInvokedClassName);
if (theInvokedClass.isOpaqueType()) {
final BytecodeResolvedMethods theMethods = theInvokedClass.resolvedMethods();
final List<BytecodeResolvedMethods.MethodEntry> theImplMethods = theMethods.stream().filter(
t -> t.getValue().getName().stringValue().equals(theMethodName) &&
t.getValue().getSignature().matchesExactlyTo(theSignature))
.collect(Collectors.toList());
if (theImplMethods.size() != 1) {
throw new IllegalStateException("Cannot find unique method " + theMethodName + " with signature " + theSignature + " in " + theInvokedClassName.name());
}
final BytecodeLinkedClass theImplClass = theImplMethods.get(0).getProvidingClass();
final BytecodeMethod theMethod = theImplMethods.get(0).getValue();
if (!theMethod.isConstructor()) {
writeOpaqueMethodInvocation(theSignature, theTarget, theArguments, theMethod);
return;
}
}
}

if (theClasses.stream().anyMatch(BytecodeLinkedClass::isOpaqueType)) {
throw new IllegalStateException("There seems to be some confusion here, either multiple OpaqueTypes with method named \"" + theMethodName + "\" or mix of Opaque and Non-Opaque virtual invocations in class list " + theClasses);
}

if (Objects.equals(aValue.getMethodName(), "invokeWithMagicBehindTheScenes")) {
writer.text("(");
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,27 @@
*/
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 @@ -130,26 +151,6 @@
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 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 @@ -1070,28 +1071,35 @@ private WASMExpression invokeVirtualValue(final InvokeVirtualMethodExpression aV
final List<Value> theVariables = theFlows.subList(1, theFlows.size());

// Check if we are invoking something on an opaque type
final BytecodeVirtualMethodIdentifier theMethodIdentifier = linkerContext.getMethodCollection().identifierFor(aValue.getMethodName(), aValue.getSignature());
final List<BytecodeLinkedClass> theClasses = linkerContext.getAllClassesAndInterfacesWithMethod(theMethodIdentifier);
if (theClasses.size() == 1) {
final BytecodeLinkedClass theTargetClass = theClasses.get(0);
final BytecodeMethod theMethod = theTargetClass.getBytecodeClass().methodByNameAndSignatureOrNull(aValue.getMethodName(), aValue.getSignature());
if (theTargetClass.isOpaqueType() && !theMethod.isConstructor()) {
// At this point, we are creating a direct call invocation to the function
// Which is imported fom the WASM Host environment
final Callable function = weakFunctionReference(WASMWriterUtils.toMethodName(theTargetClass.getClassName(), aValue.getMethodName(), aValue.getSignature()), aValue);
final List<WASMValue> arguments = new ArrayList<>();
arguments.add(toValue(theTarget));
for (final Value theValue : theVariables) {
arguments.add(toValue(theValue));
final BytecodeTypeRef theInvokedClassName = aValue.getInvokedClass();
if (!theInvokedClassName.isPrimitive() && !theInvokedClassName.isArray()) {
final BytecodeLinkedClass theInvokedClass = linkerContext.resolveClass((BytecodeObjectTypeRef) theInvokedClassName);
if (theInvokedClass.isOpaqueType()) {
final BytecodeResolvedMethods theMethods = theInvokedClass.resolvedMethods();
final List<BytecodeResolvedMethods.MethodEntry> theImplMethods = theMethods.stream().filter(
t -> t.getValue().getName().stringValue().equals(aValue.getMethodName()) &&
t.getValue().getSignature().matchesExactlyTo(aValue.getSignature()))
.collect(Collectors.toList());
if (theImplMethods.size() != 1) {
throw new IllegalStateException("Cannot find unique method " + aValue.getMethodName() + " with signature " + aValue.getSignature() + " in " + theInvokedClassName.name());
}
final BytecodeLinkedClass theImplClass = theImplMethods.get(0).getProvidingClass();
final BytecodeMethod theMethod = theImplMethods.get(0).getValue();
if (!theMethod.isConstructor()) {
// At this point, we are creating a direct call invocation to the function
// Which is imported fom the WASM Host environment
final Callable function = weakFunctionReference(WASMWriterUtils
.toMethodName(theImplClass.getClassName(), aValue.getMethodName(), aValue.getSignature()), aValue);
final List<WASMValue> arguments = new ArrayList<>();
arguments.add(toValue(theTarget));
for (final Value theValue : theVariables) {
arguments.add(toValue(theValue));
}
return call(function, arguments, aValue);
}
return call(function, arguments, aValue);
}
}

if (theClasses.stream().anyMatch(BytecodeLinkedClass::isOpaqueType)) {
throw new IllegalStateException("There seems to be some confusion here, either multiple OpaqueTypes with method named \"" + aValue.getMethodName() + "\" or mix of Opaque and Non-Opaque virtual invocations in class list " + theClasses);
}

final List<PrimitiveType> theSignatureParams = new ArrayList<>();
theSignatureParams.add(PrimitiveType.i32);
for (int i = 0; i < aValue.getSignature().getArguments().length; i++) {
Expand All @@ -1112,6 +1120,8 @@ private WASMExpression invokeVirtualValue(final InvokeVirtualMethodExpression aV
theArguments.add(toValue(theValue));
}

final BytecodeVirtualMethodIdentifier theMethodIdentifier = linkerContext.getMethodCollection().identifierFor(aValue.getMethodName(), aValue.getSignature());

final WASMType theResolveType = module.getTypes().typeFor(Arrays.asList(PrimitiveType.i32, PrimitiveType.i32), PrimitiveType.i32);
final List<WASMValue> theResolveArgument = new ArrayList<>();
theResolveArgument.add(toValue(theTarget));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@

import de.mirkosertic.bytecoder.core.BytecodeMethodSignature;
import de.mirkosertic.bytecoder.core.BytecodeNameAndTypeConstant;
import de.mirkosertic.bytecoder.core.BytecodeObjectTypeRef;
import de.mirkosertic.bytecoder.core.BytecodeOpcodeAddress;
import de.mirkosertic.bytecoder.core.BytecodeTypeRef;

public class InvokeVirtualMethodExpression extends InvocationExpression {

private final String methodName;
private final boolean interfaceInvocation;
private final BytecodeObjectTypeRef invokedClass;
private final BytecodeTypeRef invokedClass;

public InvokeVirtualMethodExpression(final Program aProgram, final BytecodeOpcodeAddress aAddress, final BytecodeNameAndTypeConstant aMethod, final Value aTarget,
final List<Value> aArguments, final boolean aInterfaceInvocation, final BytecodeObjectTypeRef aInvokedClass) {
final List<Value> aArguments, final boolean aInterfaceInvocation, final BytecodeTypeRef aInvokedClass) {
this(aProgram, aAddress, aMethod.getNameIndex().getName().stringValue(), aMethod.getDescriptorIndex().methodSignature(),
aTarget, aArguments, aInterfaceInvocation, aInvokedClass);
}

public InvokeVirtualMethodExpression(final Program aProgram, final BytecodeOpcodeAddress aAddress, final String aMethodName, final BytecodeMethodSignature aSignature, final Value aTarget,
final List<Value> aArguments, final boolean aInterfaceInvocation, final BytecodeObjectTypeRef aInvokedClass) {
final List<Value> aArguments, final boolean aInterfaceInvocation, final BytecodeTypeRef aInvokedClass) {
super(aProgram, aAddress, aSignature);
methodName = aMethodName;
interfaceInvocation = aInterfaceInvocation;
Expand All @@ -53,7 +53,7 @@ public boolean isInterfaceInvocation() {
return interfaceInvocation;
}

public BytecodeObjectTypeRef getInvokedClass() {
public BytecodeTypeRef getInvokedClass() {
return invokedClass;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
*/
package de.mirkosertic.bytecoder.ssa;

import java.lang.invoke.CallSite;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import de.mirkosertic.bytecoder.classlib.Array;
import de.mirkosertic.bytecoder.core.BytecodeArrayTypeRef;
import de.mirkosertic.bytecoder.core.BytecodeBasicBlock;
Expand Down Expand Up @@ -126,17 +137,6 @@
import de.mirkosertic.bytecoder.graph.Edge;
import de.mirkosertic.bytecoder.intrinsics.Intrinsics;

import java.lang.invoke.CallSite;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

public final class NaiveProgramGenerator implements ProgramGenerator {

public final static ProgramGeneratorFactory FACTORY = NaiveProgramGenerator::new;
Expand Down Expand Up @@ -1015,7 +1015,13 @@ theTarget, new NewObjectAndConstructExpression(

} else if (theInstruction instanceof BytecodeInstructionINVOKEVIRTUAL) {
final BytecodeInstructionINVOKEVIRTUAL theINS = (BytecodeInstructionINVOKEVIRTUAL) theInstruction;
final BytecodeObjectTypeRef theInvokedClass = BytecodeObjectTypeRef.fromUtf8Constant(theINS.getMethodReference().getClassIndex().getClassConstant().getConstant());
final BytecodeTypeRef theInvokedClass;
final BytecodeUtf8Constant theConstant = theINS.getMethodReference().getClassIndex().getClassConstant().getConstant();
if (theConstant.stringValue().startsWith("[")) {
theInvokedClass = linkerContext.getSignatureParser().toFieldType(theConstant);
} else {
theInvokedClass = BytecodeObjectTypeRef.fromUtf8Constant(theConstant);
}
final BytecodeMethodSignature theSignature = theINS.getMethodReference().getNameAndTypeIndex().getNameAndType().getDescriptorIndex().methodSignature();

final List<Value> theArguments = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
*/
package de.mirkosertic.bytecoder.core;

import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

import de.mirkosertic.bytecoder.api.web.Console;
import de.mirkosertic.bytecoder.api.web.Event;
import de.mirkosertic.bytecoder.api.web.EventListener;
Expand All @@ -30,10 +35,6 @@
import de.mirkosertic.bytecoder.api.web.Window;
import de.mirkosertic.bytecoder.unittest.BytecoderTestOptions;
import de.mirkosertic.bytecoder.unittest.BytecoderUnitTestRunner;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(BytecoderUnitTestRunner.class)
@BytecoderTestOptions(includeJVM = false)
Expand Down Expand Up @@ -129,4 +130,19 @@ public void testInt8Array() {
a.setByte(1, (byte) 99);
Assert.assertEquals((byte) 99, a.getByte(1), 0);
}

private static class Logger {

public void log(final String aValue) {
System.out.println(aValue);
}
}

@Test
public void testNameConflict() {
final Logger l = new Logger();
l.log("Hello world!");
final Console c = Console.console();
c.log("Hello console!");
}
}

0 comments on commit 8e93406

Please sign in to comment.