Skip to content

Commit

Permalink
Merge pull request #5 from electronicarts/bugfix/aconstnull
Browse files Browse the repository at this point in the history
Fix: aconst_null issue #4
  • Loading branch information
DanielSperry committed Feb 12, 2016
2 parents 990ee85 + 538968c commit 230e40a
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 10 deletions.
7 changes: 6 additions & 1 deletion async/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,12 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public BasicValue merge(BasicValue v, BasicValue w)
// TODO: test this with an assignment
// like: local1 was CompletableFuture <- store Task
ExtendedValue nv = (ExtendedValue) newValue(BasicValue.REFERENCE_VALUE.getType());
nv.undecided = new BasicValue[]{v, w};
nv.undecided = new BasicValue[]{ v, w };
return nv;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public class Transformer implements ClassFileTransformer
private static final String COMPLETION_STAGE_RET = ")Ljava/util/concurrent/CompletionStage;";

private static final String _THIS = "_this";
private static final Type ACONST_NULL_TYPE = Type.getObjectType("null");

public static final String JOIN_METHOD_NAME = "join";
public static final String JOIN_METHOD_DESC = "()Ljava/lang/Object;";
Expand Down Expand Up @@ -740,6 +741,10 @@ public void visitInsn(final int opcode)

private Argument mapLocalToLambdaArgument(final MethodNode originalMethod, SwitchEntry se, final List<Argument> arguments, final int local, final BasicValue value)
{
if (ACONST_NULL_TYPE.equals(value.getType()))
{
return null;
}
Argument argument = null;
String name = local < originalMethod.maxLocals ? guessName(originalMethod, se, local) : "_stack$" + (local - originalMethod.maxLocals);

Expand Down Expand Up @@ -1383,7 +1388,12 @@ private Object toFrameType(final BasicValue value)
case Type.DOUBLE:
return Opcodes.DOUBLE;
}
return type.getInternalName();
final String internalName = type.getInternalName();
if (ACONST_NULL_TYPE.equals(type))
{
return Opcodes.NULL;
}
return internalName;
}

private boolean isAwaitCall(final MethodInsnNode methodIns)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
/**
* For development use only
*/
class DevDebug
public class DevDebug
{
static void printMethod(final ClassNode cn, final MethodNode mv)
public static void printMethod(final ClassNode cn, final MethodNode mv)
{
final PrintWriter pw = new PrintWriter(System.out);
pw.println("method " + mv.name + mv.desc);
Expand Down Expand Up @@ -120,7 +120,7 @@ static void printMethod(final ClassNode cn, final MethodNode mv)
}


static void debugSave(final ClassNode classNode, final byte[] bytes)
public static void debugSave(final ClassNode classNode, final byte[] bytes)
{
try
{
Expand All @@ -136,7 +136,7 @@ static void debugSave(final ClassNode classNode, final byte[] bytes)
}


static void debugSaveTrace(String name, final byte[] bytes)
public static void debugSaveTrace(String name, final byte[] bytes)
{
try
{
Expand All @@ -145,7 +145,7 @@ static void debugSaveTrace(String name, final byte[] bytes)
new ClassReader(bytes).accept(new TraceClassVisitor(pw), 0);
pw.flush();

Path path = Paths.get("target/classes2/" + name + ".trace.jasm");
Path path = Paths.get("target/classes2/" + name + ".asmtrace");
Files.createDirectories(path.getParent());
Files.write(path, sw.toString().getBytes(Charset.forName("UTF-8")));
System.out.println("Writing file to " + path.toUri());
Expand All @@ -156,7 +156,7 @@ static void debugSaveTrace(String name, final byte[] bytes)
}
}

static void debugSaveTrace(String name, ClassNode node)
public static void debugSaveTrace(String name, ClassNode node)
{
try
{
Expand All @@ -165,9 +165,10 @@ static void debugSaveTrace(String name, ClassNode node)
node.accept(new TraceClassVisitor(pw));
pw.flush();

Path path = Paths.get("target/classes2/" + name + ".trace.txt");
Path path = Paths.get("target/classes2/" + name + ".asmtrace");
Files.createDirectories(path.getParent());
Files.write(path, sw.toString().getBytes(Charset.forName("UTF-8")));
System.out.println("Writing file to " + path.toUri());
}
catch (IOException e)
{
Expand Down
182 changes: 182 additions & 0 deletions async/src/test/java/com/ea/async/test/AConstNullTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
Copyright (C) 2015 Electronic Arts Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.ea.async.test;

import com.ea.async.Task;

import org.junit.Test;

import static com.ea.async.Async.await;
import static org.junit.Assert.assertNull;

public class AConstNullTest extends BaseTest
{

@Test
public void nullInitialization() throws Exception
{
final Task<Void> res = NullInit.doIt(getBlockedTask());
completeFutures();
assertNull(res.join());

}

public static class NullInit
{
public static Task<Void> doIt(Task<Void> task)
{
String x = null;
try
{
await(task);
return Task.done();
}
catch (Exception ex)
{
return Task.done();
}
}
}

@Test
public void nullInitialization2() throws Exception
{
final Task<Void> res = NullInit2.doIt(getBlockedTask());
completeFutures();
assertNull(res.join());

}

public static class NullInit2
{
public static Task<Void> doIt(Task<Void> task)
{
String x = null;
String y = x;
try
{
await(task);
return Task.done();
}
catch (Exception ex)
{
return Task.done();
}
}
}

@Test
public void nullInitialization3() throws Exception
{
final Task<Void> res = NullInit3.doIt(getBlockedTask());
completeFutures();
assertNull(res.join());

}

public static class NullInit3
{
public static Task<Void> doIt(Task<Void> task)
{
String x = null;
call0(x, await(task));
return Task.done();
}
}

public static void call0(final Object a, final Object b)
{
}


@Test
public void nullInitialization4() throws Exception
{
final Task<Void> res = NullInit4.doIt(getBlockedTask());
completeFutures();
assertNull(res.join());

}

public static class NullInit4
{
public static Task<Void> doIt(Task<String> task)
{
String x = null;
call0(await(task), x);
return Task.done();
}
}


@Test
public void nullInTheStack() throws Exception
{
debugTransform(AConstNullTest.class.getName() + "$NullInTheStack");
final Task<Void> res = NullInTheStack.doIt(getBlockedTask());
completeFutures();
assertNull(res.join());
}

public static class NullInTheStack
{
public static Task<Void> doIt(Task<Void> task)
{
call2(null, await(task));
return Task.done();
}

private static void call2(final Object o, final Object p)
{
}
}


@Test
public void nullInTheStack2() throws Exception
{
debugTransform(AConstNullTest.class.getName() + "$NullInTheStack2");
final Task<Void> res = NullInTheStack2.doIt(getBlockedTask());
completeFutures();
assertNull(res.join());
}

public static class NullInTheStack2
{
public static Task<Void> doIt(Task<Void> task)
{
call2(null, await(task));
return Task.done();
}
private static void call2(final String o, final Object p)
{
}

}
}
56 changes: 56 additions & 0 deletions async/src/test/java/com/ea/async/test/BaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@

import com.ea.async.Async;
import com.ea.async.Task;
import com.ea.async.instrumentation.DevDebug;
import com.ea.async.instrumentation.Transformer;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.objectweb.asm.ClassReader;
Expand All @@ -39,7 +42,12 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -276,4 +284,52 @@ private boolean equalsUtf8(final ClassReader cr, final int pointerToUtf8Index, f
}
return false;
}

public void debugTransform(String className) throws Exception
{

final String resourceName = "/" + className.replace(".", "/") + ".class";
System.out.println(resourceName);
final byte[] bytesInput = IOUtils.toByteArray(getClass().getResourceAsStream(resourceName));
debugTransform(bytesInput);
}


public void debugTransform(Class<?> clazz) throws Exception
{
final byte[] bytesInput = IOUtils.toByteArray(clazz.getResourceAsStream("/" + clazz.getName().replace(".", "/") + ".class"));
debugTransform(bytesInput);
}

private void debugTransform(final byte[] bytesInput) throws IOException, AnalyzerException
{
final ClassReader crInput = new ClassReader(bytesInput);
{
final ClassNode cnInput = new ClassNode();
crInput.accept(cnInput, 0);


final Path pathInput = Paths.get("target/classes2").resolve(cnInput.name + ".class");
Files.deleteIfExists(pathInput);
Files.createDirectories(pathInput.getParent());
Files.write(pathInput, bytesInput);
System.out.println(pathInput.toUri());
DevDebug.debugSaveTrace(cnInput.name, cnInput);
}

{
final Transformer transformer = new Transformer();
final byte[] bytesOutput = transformer.transform(getClass().getClassLoader(), crInput);
final ClassReader crOutput = new ClassReader(bytesOutput);
final ClassNode cnOutput = new ClassNode();
crOutput.accept(cnOutput, 0);

final Path pathOutput = Paths.get("target/classes2").resolve(cnOutput.name + ".out.class");
Files.deleteIfExists(pathOutput);
Files.createDirectories(pathOutput.getParent());
Files.write(pathOutput, bytesOutput);
System.out.println(pathOutput.toUri());
DevDebug.debugSaveTrace(cnOutput.name + ".out", cnOutput);
}
}
}

0 comments on commit 230e40a

Please sign in to comment.