Skip to content

Commit

Permalink
Add experimental option '-XX-world-cache-mode'
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangt2333 committed Jun 11, 2023
1 parent 6e39bfc commit aa2562f
Show file tree
Hide file tree
Showing 43 changed files with 734 additions and 54 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# macOS hidden file
.DS_Store
.gradle/
.idea/
bin/
build/
cache/
out/
output/

# MacOS hidden file
.DS_Store
6 changes: 3 additions & 3 deletions src/main/java/pascal/taie/AbstractWorldBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ protected static String getClassPath(Options options) {
}

protected static NativeModel getNativeModel(
TypeSystem typeSystem, ClassHierarchy hierarchy) {
return World.get().getOptions().enableNativeModel() ?
new DefaultNativeModel(typeSystem, hierarchy) :
TypeSystem typeSystem, ClassHierarchy hierarchy, Options options) {
return options.enableNativeModel() ?
new DefaultNativeModel(typeSystem, hierarchy, options.getJavaVersion()) :
new EmptyNativeModel();
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/pascal/taie/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import pascal.taie.config.Plan;
import pascal.taie.config.PlanConfig;
import pascal.taie.config.Scope;
import pascal.taie.frontend.cache.CachedWorldBuilder;
import pascal.taie.util.Timer;
import pascal.taie.util.collection.Lists;

Expand Down Expand Up @@ -125,6 +126,9 @@ private static void buildWorld(Options options, List<AnalysisConfig> analyses) {
Class<? extends WorldBuilder> builderClass = options.getWorldBuilderClass();
Constructor<? extends WorldBuilder> builderCtor = builderClass.getConstructor();
WorldBuilder builder = builderCtor.newInstance();
if (options.isWorldCacheMode()) {
builder = new CachedWorldBuilder(builder);
}
builder.build(options, analyses);
logger.info("{} classes with {} methods in the world",
World.get()
Expand Down
37 changes: 34 additions & 3 deletions src/main/java/pascal/taie/World.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@
package pascal.taie;

import pascal.taie.config.Options;
import pascal.taie.frontend.cache.CachedIRBuilder;
import pascal.taie.ir.IRBuilder;
import pascal.taie.language.classes.ClassHierarchy;
import pascal.taie.language.classes.JMethod;
import pascal.taie.language.natives.NativeModel;
import pascal.taie.language.type.TypeSystem;
import pascal.taie.util.AbstractResultHolder;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
Expand All @@ -39,7 +45,8 @@
* Note that the setters of this class are protected: they are supposed
* to be called (once) by the world builder, not analysis classes.
*/
public final class World extends AbstractResultHolder {
public final class World extends AbstractResultHolder
implements Serializable {

/**
* ZA WARUDO, i.e., the current world.
Expand All @@ -52,13 +59,24 @@ public final class World extends AbstractResultHolder {
*/
private static final List<Runnable> resetCallbacks = new ArrayList<>();

private Options options;
/**
* Notes: This field is {@code transient} because it
* should be set after deserialization.
*/
private transient Options options;

private TypeSystem typeSystem;

private ClassHierarchy classHierarchy;

private IRBuilder irBuilder;
/**
* Notes: add {@code transient} to wrap this {@link IRBuilder} using
* {@link pascal.taie.frontend.cache.CachedIRBuilder} in serialization.
*
* @see #writeObject(ObjectOutputStream)
* @see #readObject(ObjectInputStream)
*/
private transient IRBuilder irBuilder;

private NativeModel nativeModel;

Expand Down Expand Up @@ -156,4 +174,17 @@ public Collection<JMethod> getImplicitEntries() {
public void setImplicitEntries(Collection<JMethod> implicitEntries) {
this.implicitEntries = implicitEntries;
}

@Serial
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeObject(new CachedIRBuilder(irBuilder, classHierarchy));
}

@Serial
private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
s.defaultReadObject();
irBuilder = (IRBuilder) s.readObject();
}
}
15 changes: 14 additions & 1 deletion src/main/java/pascal/taie/config/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Path;
import java.time.Instant;
import java.time.ZoneId;
Expand All @@ -51,7 +52,7 @@
description = "Tai-e options",
usageHelpWidth = 120
)
public class Options {
public class Options implements Serializable {

private static final Logger logger = LogManager.getLogger(Options.class);

Expand Down Expand Up @@ -195,6 +196,17 @@ public boolean isPreBuildIR() {
return preBuildIR;
}

@JsonProperty
@Option(names = "-XX-world-cache-mode",
description = "Experimental Feature: Enable world cache mode to save build time"
+ " by caching the completed built world to the disk.",
defaultValue = "false")
private boolean worldCacheMode;

public boolean isWorldCacheMode() {
return worldCacheMode;
}

@JsonProperty
@Option(names = "-scope",
description = "Scope for method/class analyses (default: ${DEFAULT-VALUE}," +
Expand Down Expand Up @@ -385,6 +397,7 @@ public String toString() {
", worldBuilderClass=" + worldBuilderClass +
", outputDir='" + outputDir + '\'' +
", preBuildIR=" + preBuildIR +
", worldCacheMode=" + worldCacheMode +
", scope=" + scope +
", nativeModel=" + nativeModel +
", planFile=" + planFile +
Expand Down
71 changes: 71 additions & 0 deletions src/main/java/pascal/taie/frontend/cache/CachedIRBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Tai-e: A Static Analysis Framework for Java
*
* Copyright (C) 2022 Tian Tan <[email protected]>
* Copyright (C) 2022 Yue Li <[email protected]>
*
* This file is part of Tai-e.
*
* Tai-e is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Tai-e is distributed in the hope that it will be useful,but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
* Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
*/

package pascal.taie.frontend.cache;


import pascal.taie.ir.IR;
import pascal.taie.ir.IRBuilder;
import pascal.taie.language.classes.ClassHierarchy;
import pascal.taie.language.classes.JClass;
import pascal.taie.language.classes.JMethod;

import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;

/**
* The {@link pascal.taie.ir.IRBuilder} is for keeping the {@link IR}s of all methods to
* prevent cyclic references with too long a path which may make
* the serialization fail or {@link java.lang.StackOverflowError}.
*/
public class CachedIRBuilder implements IRBuilder {

private final Map<String, IR> methodSig2IR;

public CachedIRBuilder(IRBuilder irBuilder, ClassHierarchy hierarchy) {
irBuilder.buildAll(hierarchy);
methodSig2IR = hierarchy.allClasses()
.map(JClass::getDeclaredMethods)
.flatMap(Collection::stream)
.filter(m -> !m.isAbstract() || m.isNative())
.collect(Collectors.toMap(JMethod::getSignature, JMethod::getIR));
}

/**
* This method will be called by {@link JMethod#getIR()} only once,
* so remove the IR from the map after returning it.
*/
@Override
public IR buildIR(JMethod method) {
return methodSig2IR.remove(method.getSignature());
}

@Override
public void buildAll(ClassHierarchy hierarchy) {
hierarchy.allClasses()
.map(JClass::getDeclaredMethods)
.flatMap(Collection::stream)
.filter(m -> !m.isAbstract() || m.isNative())
.forEach(JMethod::getIR);
}
}
Loading

0 comments on commit aa2562f

Please sign in to comment.