Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions approximations/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
@file:Suppress("VulnerableLibrariesLocal")

plugins {
java
`maven-publish`
id("org.springframework.boot") version "3.2.0" apply false
}

repositories {
Expand All @@ -10,15 +11,12 @@ repositories {
}

private val jacodbPackage = "com.github.UnitTestBot.jacodb"
private val jacodbVersion = "00164e304b" // jacodb neo branch
private val jacodbVersion = "453ec7c0b3" // jacodb neo branch

dependencies {
compileOnly("$jacodbPackage:jacodb-api-jvm:$jacodbVersion")
compileOnly("$jacodbPackage:jacodb-approximations:$jacodbVersion")
compileOnly(files(rootDir.resolve("usvm-api/usvm-api.jar")))
compileOnly("org.springframework.boot:spring-boot-starter-web:3.2.0")
compileOnly("org.springframework.boot:spring-boot-starter-test:3.2.0")
compileOnly("org.springframework.boot:spring-boot-starter-data-jpa:3.2.0")
compileOnly(files(rootDir.resolve("usvm-api/usvm-jvm-api.jar")))
// Fixes "unknown enum constant 'When.MAYBE'" warning
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,28 @@

@DecoderFor(Integer.class)
public final class Integer_Decoder implements ObjectDecoder {
private volatile JcMethod cached_Integer_ctor = null;
private volatile JcMethod cached_Integer_valueOf = null;
private volatile JcField cached_Integer_value = null;

@Override
public <T> T createInstance(final JcClassOrInterface approximation,
final ObjectData<T> approximationData,
final DecoderApi<T> decoder) {
JcMethod ctor = cached_Integer_ctor;
JcMethod valueOf = cached_Integer_valueOf;
// TODO: add synchronization if needed
if (ctor == null) {
if (valueOf == null) {
// looking for constructor and data field
final List<JcMethod> methods = approximation.getDeclaredMethods();
for (int i = 0, c = methods.size(); i != c; i++) {
JcMethod m = methods.get(i);

if (!m.isConstructor()) continue;
if (!"valueOf".equals(m.getName())) continue;

List<JcParameter> params = m.getParameters();
if (params.size() != 1) continue;
if (!"int".equals(params.get(0).getType().getTypeName())) continue;

cached_Integer_ctor = ctor = m;
cached_Integer_valueOf = valueOf = m;
break;
}

Expand All @@ -51,7 +51,7 @@ public <T> T createInstance(final JcClassOrInterface approximation,

// assemble into a call
final List<T> args = Collections.singletonList(decoder.createIntConst(value));
return decoder.invokeMethod(ctor, args);
return decoder.invokeMethod(valueOf, args);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package decoders.java.util;

import org.jacodb.api.jvm.JcClassOrInterface;
import org.jacodb.api.jvm.JcField;
import org.usvm.api.decoder.*;
import stub.java.util.map.AbstractMap_Entry;

import java.util.List;

@DecoderFor(AbstractMap_Entry.class)
public class AbstractMap_Entry_Decoder implements ObjectDecoder {
private volatile JcField cached_Entry_value = null;

@SuppressWarnings({"ForLoopReplaceableByForEach", "unchecked"})
@Override
public <T> T createInstance(final JcClassOrInterface approx,
final ObjectData<T> approxData,
final DecoderApi<T> decoder) {
JcField f_value = cached_Entry_value;
// TODO: add synchronization if needed
if (f_value == null) {
final List<JcField> fields = approx.getDeclaredFields();
for (int i = 0, c = fields.size(); i < c; i++) {
JcField f = fields.get(i);
if ("value".equals(f.getName())) {
cached_Entry_value = f_value = f;
break;
}
}
}

T value = approxData.decodeField(f_value);
return (T) new InternalMapEntry<T, T>(value);
}

@Override
public <T> void initializeInstance(final JcClassOrInterface approx,
final ObjectData<T> approxData,
final T instance,
final DecoderApi<T> decoder) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.util.Collections;
import java.util.List;

import static org.usvm.api.decoder.DecoderUtils.findStorageField;

@SuppressWarnings("ForLoopReplaceableByForEach")
@DecoderFor(ArrayList.class)
public class ArrayList_Decoder implements ObjectDecoder {
Expand Down Expand Up @@ -46,7 +48,6 @@ public <T> T createInstance(final JcClassOrInterface approx,

List<JcParameter> params = m.getParameters();
if (params.size() != 1) continue;
if (!"java.lang.Object".equals(params.get(0).getType().getTypeName())) continue;

cached_ArrayList_add = m_add = m;
}
Expand All @@ -68,14 +69,7 @@ public <T> void initializeInstance(final JcClassOrInterface approx,
JcField f_storage = cached_ArrayList_storage;
// TODO: add synchronization if needed
if (f_storage == null) {
final List<JcField> fields = approx.getDeclaredFields();
for (int i = 0, c = fields.size(); i < c; i++) {
JcField f = fields.get(i);
if ("storage".equals(f.getName())) {
cached_ArrayList_storage = f_storage = f;
break;
}
}
cached_ArrayList_storage = f_storage = findStorageField(approx);
}

if (approxData.getObjectField(f_storage) == null)
Expand Down
154 changes: 154 additions & 0 deletions approximations/src/main/java/decoders/java/util/HashMap_Decoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package decoders.java.util;

import org.jacodb.api.jvm.*;
import org.usvm.api.SymbolicMap;
import org.usvm.api.decoder.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static org.usvm.api.decoder.DecoderUtils.findStorageField;
import static org.usvm.api.decoder.DecoderUtils.getAllMethods;

@DecoderFor(HashMap.class)
public class HashMap_Decoder implements ObjectDecoder {
private volatile JcMethod[] cachedMethods = null;
private volatile JcMethod cached_HashMap_ctor = null;
private volatile JcMethod cached_HashMap_put = null;
private volatile JcField cached_HashMap_storage = null;
private volatile JcField cached_Map_map = null;
private volatile JcField cached_HashMapContainer_map = null;
private volatile JcMethod cached_MapEntry_getValue = null;

@Override
public <T> T createInstance(final JcClassOrInterface approximation,
final ObjectData<T> approximationData,
final DecoderApi<T> decoder) {
JcMethod m_ctor = cached_HashMap_ctor;
// TODO: add synchronization if needed
if (m_ctor == null) {
final List<JcMethod> methodList = approximation.getDeclaredMethods();
final int methodCount = methodList.size();
JcMethod[] methods = new JcMethod[methodCount];
cachedMethods = methods = methodList.toArray(methods);

for (int i = 0; i != methodCount; i++) {
JcMethod m = methods[i];

if (m.isConstructor()) {
List<JcParameter> params = m.getParameters();

// example: looking for java.util.HashMap.HashMap()
if (!params.isEmpty()) continue;

// update global "cache" and stop the search
cached_HashMap_ctor = m_ctor = m;
break;
}
}
}

// prepare parameters "in-place" and construct a new call
final ArrayList<T> args = new ArrayList<>();
return decoder.invokeMethod(m_ctor, args);
}

@SuppressWarnings("unchecked")
@Override
public <T> void initializeInstance(final JcClassOrInterface approximation,
final ObjectData<T> approximationData,
final T outputInstance,
final DecoderApi<T> decoder) {
JcField f_hs_storage = cached_HashMap_storage;
// TODO: add synchronization if needed
if (f_hs_storage == null) {
cached_HashMap_storage = f_hs_storage = findStorageField(approximation);
}

// skip empty or erroneous objects
final ObjectData<T> storageData = approximationData.getObjectField(f_hs_storage);
if (storageData == null)
return;

// get primary method
JcMethod m_put = cached_HashMap_put;
// TODO: add synchronization if needed
if (m_put == null) {
// TODO: add cache
List<JcMethod> methodsList = getAllMethods(approximation);
final int methodCount = methodsList.size();
JcMethod[] methods = new JcMethod[methodCount];
methodsList.toArray(methods);
for (int i = 0, c = methods.length; i != c; i++) {
JcMethod m = methods[i];

if (!"put".equals(m.getName())) continue;
List<JcParameter> params = m.getParameters();
if (params.size() != 2) continue;

m_put = m;
break;
}
cached_HashMap_put = m_put;
}

// prepare field references (inlined)
JcField f_m_map = cached_Map_map;
// TODO: add synchronization if needed
if (f_m_map == null) {
JcClasspath cp = approximation.getClasspath();
{
List<JcField> fields = cp.findClassOrNull("runtime.LibSLRuntime$Map").getDeclaredFields();
for (int i = 0, c = fields.size(); i != c; i++) {
JcField field = fields.get(i);

if ("map".equals(field.getName())) {
cached_Map_map = f_m_map = field;
break;
}
}
}
{
List<JcField> fields = cp.findClassOrNull("runtime.LibSLRuntime$HashMapContainer").getDeclaredFields();
for (int i = 0, c = fields.size(); i != c; i++) {
JcField field = fields.get(i);

if ("map".equals(field.getName())) {
cached_HashMapContainer_map = field;
break;
}
}
}
}

// get and parse the underlying symbolic map
final ObjectData<T> rtMapContainerData = storageData.getObjectField(f_m_map);
if (rtMapContainerData == null)
return; // ignore invalid container

final SymbolicMap<T, T> map = rtMapContainerData.decodeSymbolicMapField(cached_HashMapContainer_map);
if (map == null)
return; // ignore invalid container

int length = map.size();
if (length == Integer.MAX_VALUE)
return; // ignore invalid container

while (length > 0) {
T key = map.anyKey();
T value = map.get(key);
if (value instanceof InternalMapEntry) {
value = ((InternalMapEntry<T, T>) value).getValue();
}

List<T> args = new ArrayList<>();
args.add(outputInstance);
args.add(key);
args.add(value);
decoder.invokeMethod(m_put, args);

map.remove(key);
length--;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import java.util.HashSet;
import java.util.List;

import static org.usvm.api.decoder.DecoderUtils.findStorageField;

@DecoderFor(HashSet.class)
public final class HashSet_Decoder implements ObjectDecoder {
private volatile JcMethod[] cachedMethods = null;
Expand Down Expand Up @@ -65,17 +67,7 @@ public <T> void initializeInstance(final JcClassOrInterface approximation,
JcField f_hs_storage = cached_HashSet_storage;
// TODO: add synchronization if needed
if (f_hs_storage == null) {
final List<JcField> fields = approximation.getDeclaredFields();
for (int i = 0, c = fields.size(); i != c; i++) {
JcField field = fields.get(i);
String fieldName = field.getName();

if (!"storage".equals(fieldName)) continue;

// early termination
cached_HashSet_storage = f_hs_storage = field;
break;
}
cached_HashSet_storage = f_hs_storage = findStorageField(approximation);
}

// skip erroneous objects
Expand Down
Loading