Skip to content

Commit

Permalink
#528 Instanceof and hashcode on any Class (without a instantion) caus…
Browse files Browse the repository at this point in the history
…es a crash (#576)
  • Loading branch information
mirkosertic authored Jun 24, 2021
1 parent b78435e commit 08b9155
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public URL getResource(final String name) {

@Override
public int hashCode() {
return getName().hashCode();
return 42;
}

public <T> T cast(final Object obj) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,8 @@ public JSCompileResult generateCodeFor(final CompileOptions aOptions, final Byte
final String theGetClassLoaderMethodName = theMinifier.toMethodName("getClassLoader", new BytecodeMethodSignature(BytecodeObjectTypeRef.fromRuntimeClass(ClassLoader.class), new BytecodeTypeRef[0]));
final String theGetClassLoaderMethodName0 = theMinifier.toMethodName("getClassLoader0", new BytecodeMethodSignature(BytecodeObjectTypeRef.fromRuntimeClass(ClassLoader.class), new BytecodeTypeRef[0]));
final String theHashCodeMethodName = theMinifier.toMethodName("hashCode", new BytecodeMethodSignature(BytecodePrimitiveTypeRef.INT, new BytecodeTypeRef[0]));
final String theEqualsMethodName = theMinifier.toMethodName("equals", new BytecodeMethodSignature(BytecodePrimitiveTypeRef.BOOLEAN, new BytecodeTypeRef[] {BytecodeObjectTypeRef.fromRuntimeClass(Object.class)}));

final String theIsAssignableFromMethodName = theMinifier.toMethodName("isAssignableFrom", new BytecodeMethodSignature(BytecodePrimitiveTypeRef.BOOLEAN, new BytecodeTypeRef[] {BytecodeObjectTypeRef.fromRuntimeClass(Class.class)}));
final String theGetConstructorMethodName = theMinifier.toMethodName("getConstructor", new BytecodeMethodSignature(BytecodeObjectTypeRef.fromRuntimeClass(
Constructor.class), new BytecodeTypeRef[] {new BytecodeArrayTypeRef(BytecodeObjectTypeRef.fromRuntimeClass(Class.class), 1)}));
Expand All @@ -890,13 +892,19 @@ public JSCompileResult generateCodeFor(final CompileOptions aOptions, final Byte
theWriter.tab(2).text("this.declaredFields").assign().text("undefined;").newLine();
theWriter.tab(2).text("this.primitive").assign().text("primitive;").newLine();
theWriter.tab(1).text("};").newLine();
theWriter.tab(1).text("C.").text(theMinifier.toSymbol("__runtimeclass")).assign().text("new C(-1,null,[],false);").newLine();
theWriter.tab(1).text("C.prototype.").text(theGetNameMethodName).assign().text("function()").space().text("{").newLine();
theWriter.tab(2).text("return bytecoder.stringpool[this.classNameIndex];").newLine();
theWriter.tab(1).text("};").newLine();

theWriter.tab(1).text("C.prototype.").text(theHashCodeMethodName).assign().text("function()").space().text("{").newLine();
theWriter.tab(2).text("return this.").text(theGetNameMethodName).text("().").text(theHashCodeMethodName).text("();").newLine();
theWriter.tab(2).text("return 42;").newLine();
theWriter.tab(1).text("};").newLine();

theWriter.tab().text("C.prototype.").text(theEqualsMethodName).assign().text("function(args)").space().text("{").newLine();
theWriter.tab(2).text("return ").text(theMinifier.toClassName(BytecodeObjectTypeRef.fromRuntimeClass(Class.class))).text(".prototype.").text(theEqualsMethodName).text(".call(this, args);").newLine();
theWriter.tab().text("};").newLine();

theWriter.tab(1).text("C.prototype.").text(theIsAssignableFromMethodName).assign().text("function(aOtherType)").space().text("{").newLine();
theWriter.tab(2).text("for (var i=0;i<aOtherType.implementedTypes.length;i++) {").newLine();
theWriter.tab(3).text("if (aOtherType.implementedTypes[i].").text(theMinifier.toSymbol("__runtimeclass")).text(" === this) {").newLine();
Expand All @@ -914,6 +922,7 @@ public JSCompileResult generateCodeFor(final CompileOptions aOptions, final Byte
theWriter.tab(1).text("};").newLine();

theWriter.tab().text("C.prototype.").text("iof").assign().text("function(aType)").space().text("{").newLine();
theWriter.tab(2).text("if (aType === ").text(theMinifier.toSymbol("jlClass")).text(") return true;").newLine();
theWriter.tab(2).text("for (var i=0;i<this.implementedTypes.length;i++) {").newLine();
theWriter.tab(3).text("if (this.implementedTypes[i].").text(theMinifier.toSymbol("__runtimeclass")).text("===aType.").text(theMinifier.toSymbol("__runtimeclass")).text(") {").newLine();
theWriter.tab(4).text("return true;").newLine();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1267,8 +1267,8 @@ public String methodHandleDelegateFor(final MethodHandleExpression e) {
pw.print(LLVMWriterUtils.toClassName(theClassLinkedCass.getClassName()));
pw.print(LLVMWriter.VTABLETYPESUFFIX);
pw.println(" {");
pw.println(" i1(i32,i32)* undef,");
pw.println(" i32(i32,i32)* undef");
pw.println(" i1(i32,i32)* @jlClass__instanceof,");
pw.println(" i32(i32,i32)* @jlClass__interfacedispatch");
for (final BytecodeVTable.Slot slot : theClassSlots) {
final BytecodeVTable.VPtr ptr = theClassVTable.slot(slot);
if (ptr.getImplementingClass() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,6 @@
*/
package de.mirkosertic.bytecoder.backend.wasm;

import static de.mirkosertic.bytecoder.backend.wasm.WASMSSAASTWriter.toType;
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.param;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionReference;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionTableReference;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
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.Stack;
import java.util.stream.Collectors;

import de.mirkosertic.bytecoder.allocator.AbstractAllocator;
import de.mirkosertic.bytecoder.allocator.Register;
import de.mirkosertic.bytecoder.api.EmulatedByRuntime;
Expand Down Expand Up @@ -104,6 +77,33 @@
import de.mirkosertic.bytecoder.stackifier.HeadToHeadControlFlowException;
import de.mirkosertic.bytecoder.stackifier.Stackifier;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
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.Stack;
import java.util.stream.Collectors;

import static de.mirkosertic.bytecoder.backend.wasm.WASMSSAASTWriter.toType;
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.param;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionReference;
import static de.mirkosertic.bytecoder.backend.wasm.ast.ConstExpressions.weakFunctionTableReference;

public class WASMSSAASTCompilerBackend implements CompileBackend<WASMCompileResult> {

private static class CallSite {
Expand Down Expand Up @@ -476,15 +476,29 @@ public String methodHandleDelegateFor(final MethodHandleExpression e) {
theMethod.flow.ret(i32.c(0, null), null);
}

final ExportableFunction runtimeInstanceOf = module.getFunctions().newFunction("RUNTIMECLASS" + WASMSSAASTWriter.INSTANCEOFSUFFIX, Arrays.asList(param("thisRef", PrimitiveType.i32), param("otherType", PrimitiveType.i32)), PrimitiveType.i32).toTable();
{
final BytecodeLinkedClass theClassLinkedCass = aLinkerContext.resolveClass(BytecodeObjectTypeRef.fromRuntimeClass(Class.class));
final Block checkblock = runtimeInstanceOf.flow.block("check", null);
checkblock.flow.branchIff(checkblock, i32.ne(getLocal(runtimeInstanceOf.localByLabel("otherType"), null), i32.c(theClassLinkedCass.getUniqueId(), null), null), null);
checkblock.flow.ret(i32.c(1, null), null);

runtimeInstanceOf.flow.ret(i32.c(0, null), null);
}

final ExportableFunction runtimeResolvevtableindex = module.getFunctions().newFunction("RUNTIMECLASS" + WASMSSAASTWriter.VTABLEFUNCTIONSUFFIX, Arrays.asList(param("thisRef", PrimitiveType.i32), param("methodId", PrimitiveType.i32)), PrimitiveType.i32).toTable();
{
final Block instanceOfBlock = runtimeResolvevtableindex.flow.block("instanceof", null);
instanceOfBlock.flow.branchIff(instanceOfBlock, i32.ne(getLocal(runtimeResolvevtableindex.localByLabel("methodId"), null), i32.c(WASMSSAASTWriter.GENERATED_INSTANCEOF_METHOD_ID, null), null), null);
instanceOfBlock.flow.ret(weakFunctionTableReference("RUNTIMECLASS" + WASMSSAASTWriter.INSTANCEOFSUFFIX, null), null);

final BytecodeLinkedClass theClassLinkedCass = aLinkerContext.resolveClass(BytecodeObjectTypeRef.fromRuntimeClass(Class.class));
final BytecodeResolvedMethods theRuntimeMethodMap = aLinkerContext.resolveMethods(theClassLinkedCass);
theRuntimeMethodMap.stream().forEach(aMethodMapEntry -> {
final BytecodeMethod theMethod = aMethodMapEntry.getValue();

if (!theMethod.getAccessFlags().isStatic() && !theMethod.isConstructor() && !theMethod.isClassInitializer() &&
aMethodMapEntry.getProvidingClass().getClassName().equals(BytecodeObjectTypeRef.fromRuntimeClass(Class.class))) {
(aMethodMapEntry.getProvidingClass().getClassName().equals(BytecodeObjectTypeRef.fromRuntimeClass(Class.class)) || (theMethod.getName().stringValue().equals("equals")))) {

final BytecodeVirtualMethodIdentifier theMethodIdentifier = aLinkerContext.getMethodCollection().identifierFor(theMethod);
final Block block = runtimeResolvevtableindex.flow.block("m" + theMethodIdentifier.getIdentifier(), null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,20 @@ public void testIsInstanceTrue() {
public void testIsNotInstance() {
Assert.assertFalse(RuntimeClassTest.class.isInstance("Hello world!"));
}

@Test
public void testInstanceOf() {
final Class clazz = String.class;
Assert.assertTrue(clazz instanceof Class);
}

@Test
public void testHashCode() {
Assert.assertEquals(42, String.class.hashCode());
}

@Test
public void testEquals() {
Assert.assertEquals(String.class, String.class);
}
}

0 comments on commit 08b9155

Please sign in to comment.