245 lines
7.4 KiB
Java
245 lines
7.4 KiB
Java
// Copyright (C) 2011 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package com.google.gerrit.server.rules;
|
|
|
|
import com.google.gerrit.server.AnonymousUser;
|
|
import com.google.gerrit.server.IdentifiedUser;
|
|
import com.google.gerrit.server.config.GerritServerConfig;
|
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
|
import com.google.gerrit.server.patch.PatchListCache;
|
|
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
|
import com.google.gerrit.server.project.ProjectCache;
|
|
import com.google.inject.Inject;
|
|
import com.google.inject.Provider;
|
|
import com.google.inject.Singleton;
|
|
import com.google.inject.assistedinject.Assisted;
|
|
import com.googlecode.prolog_cafe.lang.BufferingPrologControl;
|
|
import com.googlecode.prolog_cafe.lang.Predicate;
|
|
import com.googlecode.prolog_cafe.lang.PredicateEncoder;
|
|
import com.googlecode.prolog_cafe.lang.Prolog;
|
|
import com.googlecode.prolog_cafe.lang.PrologMachineCopy;
|
|
import java.util.EnumSet;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import org.eclipse.jgit.lib.Config;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
/**
|
|
* Per-thread Prolog interpreter.
|
|
*
|
|
* <p>This class is not thread safe.
|
|
*
|
|
* <p>A single copy of the Prolog interpreter, for the current thread.
|
|
*/
|
|
public class PrologEnvironment extends BufferingPrologControl {
|
|
private static final Logger log = LoggerFactory.getLogger(PrologEnvironment.class);
|
|
|
|
public interface Factory {
|
|
/**
|
|
* Construct a new Prolog interpreter.
|
|
*
|
|
* @param src the machine to template the new environment from.
|
|
* @return the new interpreter.
|
|
*/
|
|
PrologEnvironment create(PrologMachineCopy src);
|
|
}
|
|
|
|
private final Args args;
|
|
private final Map<StoredValue<Object>, Object> storedValues;
|
|
private List<Runnable> cleanup;
|
|
|
|
@Inject
|
|
PrologEnvironment(Args a, @Assisted PrologMachineCopy src) {
|
|
super(src);
|
|
setEnabled(EnumSet.allOf(Prolog.Feature.class), false);
|
|
args = a;
|
|
storedValues = new HashMap<>();
|
|
cleanup = new LinkedList<>();
|
|
}
|
|
|
|
public Args getArgs() {
|
|
return args;
|
|
}
|
|
|
|
@Override
|
|
public void setPredicate(Predicate goal) {
|
|
super.setPredicate(goal);
|
|
setReductionLimit(args.reductionLimit(goal));
|
|
}
|
|
|
|
/**
|
|
* Lookup a stored value in the interpreter's hash manager.
|
|
*
|
|
* @param <T> type of stored Java object.
|
|
* @param sv unique key.
|
|
* @return the value; null if not stored.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public <T> T get(StoredValue<T> sv) {
|
|
return (T) storedValues.get(sv);
|
|
}
|
|
|
|
/**
|
|
* Set a stored value on the interpreter's hash manager.
|
|
*
|
|
* @param <T> type of stored Java object.
|
|
* @param sv unique key.
|
|
* @param obj the value to store under {@code sv}.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public <T> void set(StoredValue<T> sv, T obj) {
|
|
storedValues.put((StoredValue<Object>) sv, obj);
|
|
}
|
|
|
|
/**
|
|
* Copy the stored values from another interpreter to this one. Also gets the cleanup from the
|
|
* child interpreter
|
|
*/
|
|
public void copyStoredValues(PrologEnvironment child) {
|
|
storedValues.putAll(child.storedValues);
|
|
setCleanup(child.cleanup);
|
|
}
|
|
|
|
/**
|
|
* Assign the environment a cleanup list (in order to use a centralized list) If this
|
|
* enivronment's list is non-empty, append its cleanup tasks to the assigning list.
|
|
*/
|
|
public void setCleanup(List<Runnable> newCleanupList) {
|
|
newCleanupList.addAll(cleanup);
|
|
cleanup = newCleanupList;
|
|
}
|
|
|
|
/**
|
|
* Adds cleanup task to run when close() is called
|
|
*
|
|
* @param task is run when close() is called
|
|
*/
|
|
public void addToCleanup(Runnable task) {
|
|
cleanup.add(task);
|
|
}
|
|
|
|
/** Release resources stored in interpreter's hash manager. */
|
|
public void close() {
|
|
for (Iterator<Runnable> i = cleanup.iterator(); i.hasNext(); ) {
|
|
try {
|
|
i.next().run();
|
|
} catch (Throwable err) {
|
|
log.error("Failed to execute cleanup for PrologEnvironment", err);
|
|
}
|
|
i.remove();
|
|
}
|
|
}
|
|
|
|
@Singleton
|
|
public static class Args {
|
|
private static final Class<Predicate> CONSULT_STREAM_2;
|
|
|
|
static {
|
|
try {
|
|
@SuppressWarnings("unchecked")
|
|
Class<Predicate> c =
|
|
(Class<Predicate>)
|
|
Class.forName(
|
|
PredicateEncoder.encode(Prolog.BUILTIN, "consult_stream", 2),
|
|
false,
|
|
RulesCache.class.getClassLoader());
|
|
CONSULT_STREAM_2 = c;
|
|
} catch (ClassNotFoundException e) {
|
|
throw new LinkageError("cannot find predicate consult_stream", e);
|
|
}
|
|
}
|
|
|
|
private final ProjectCache projectCache;
|
|
private final PermissionBackend permissionBackend;
|
|
private final GitRepositoryManager repositoryManager;
|
|
private final PatchListCache patchListCache;
|
|
private final PatchSetInfoFactory patchSetInfoFactory;
|
|
private final IdentifiedUser.GenericFactory userFactory;
|
|
private final Provider<AnonymousUser> anonymousUser;
|
|
private final int reductionLimit;
|
|
private final int compileLimit;
|
|
|
|
@Inject
|
|
Args(
|
|
ProjectCache projectCache,
|
|
PermissionBackend permissionBackend,
|
|
GitRepositoryManager repositoryManager,
|
|
PatchListCache patchListCache,
|
|
PatchSetInfoFactory patchSetInfoFactory,
|
|
IdentifiedUser.GenericFactory userFactory,
|
|
Provider<AnonymousUser> anonymousUser,
|
|
@GerritServerConfig Config config) {
|
|
this.projectCache = projectCache;
|
|
this.permissionBackend = permissionBackend;
|
|
this.repositoryManager = repositoryManager;
|
|
this.patchListCache = patchListCache;
|
|
this.patchSetInfoFactory = patchSetInfoFactory;
|
|
this.userFactory = userFactory;
|
|
this.anonymousUser = anonymousUser;
|
|
|
|
int limit = config.getInt("rules", null, "reductionLimit", 100000);
|
|
reductionLimit = limit <= 0 ? Integer.MAX_VALUE : limit;
|
|
|
|
limit =
|
|
config.getInt(
|
|
"rules",
|
|
null,
|
|
"compileReductionLimit",
|
|
(int) Math.min(10L * limit, Integer.MAX_VALUE));
|
|
compileLimit = limit <= 0 ? Integer.MAX_VALUE : limit;
|
|
}
|
|
|
|
private int reductionLimit(Predicate goal) {
|
|
if (goal.getClass() == CONSULT_STREAM_2) {
|
|
return compileLimit;
|
|
}
|
|
return reductionLimit;
|
|
}
|
|
|
|
public ProjectCache getProjectCache() {
|
|
return projectCache;
|
|
}
|
|
|
|
public PermissionBackend getPermissionBackend() {
|
|
return permissionBackend;
|
|
}
|
|
|
|
public GitRepositoryManager getGitRepositoryManager() {
|
|
return repositoryManager;
|
|
}
|
|
|
|
public PatchListCache getPatchListCache() {
|
|
return patchListCache;
|
|
}
|
|
|
|
public PatchSetInfoFactory getPatchSetInfoFactory() {
|
|
return patchSetInfoFactory;
|
|
}
|
|
|
|
public IdentifiedUser.GenericFactory getUserFactory() {
|
|
return userFactory;
|
|
}
|
|
|
|
public AnonymousUser getAnonymousUser() {
|
|
return anonymousUser.get();
|
|
}
|
|
}
|
|
}
|