ProgramBuilder.java
/*
* Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Additionally licensed with:
*
* 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 org.spf4j.zel.vm;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import javax.annotation.Nullable;
import org.spf4j.base.Pair;
import org.spf4j.zel.instr.CALLA;
import org.spf4j.zel.instr.Instruction;
import org.spf4j.zel.vm.ParsingContext.Location;
/**
*
* @author zoly
*/
@SuppressFBWarnings("FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY")
final class ProgramBuilder {
private static final int DEFAULT_SIZE = 16;
private static final AtomicInteger COUNTER = new AtomicInteger();
private Instruction[] instructions;
private final List<Location> debugInfo;
private int instrNumber;
private Program.Type type;
private Program.ExecutionType execType;
private final Interner<String> stringInterner;
private final MemoryBuilder staticMemBuilder;
public static int generateID() {
return COUNTER.getAndIncrement();
}
/**
* initializes the program
*/
ProgramBuilder(final MemoryBuilder staticMemBuilder) {
this.staticMemBuilder = staticMemBuilder;
instructions = new Instruction[DEFAULT_SIZE];
instrNumber = 0;
type = Program.Type.NONDETERMINISTIC;
execType = null; //Program.ExecutionType.ASYNC;
stringInterner = Interners.newStrongInterner();
debugInfo = new ArrayList<>();
}
public void intern(final Object[] array) {
for (int i = 0; i < array.length; i++) {
Object obj = array[i];
if (obj instanceof String) {
array[i] = stringInterner.intern((String) obj);
}
}
}
/**
* @return the type
*/
public Program.Type getType() {
return type;
}
/**
* @param type the type to set
*/
public ProgramBuilder setType(final Program.Type ptype) {
this.type = ptype;
return this;
}
public ProgramBuilder setExecType(final Program.ExecutionType pexecType) {
this.execType = pexecType;
return this;
}
public ProgramBuilder add(final Instruction object, final Location loc) {
int nSize = instrNumber + 1;
ensureCapacity(nSize);
instructions[instrNumber] = object;
instrNumber = nSize;
debugInfo.add(loc);
return this;
}
public boolean contains(final Class<? extends Instruction> instr) {
Boolean res = itterate(new Program.HasClass(instr));
if (res == null) {
return false;
}
return res;
}
@Nullable
public <T> T itterate(final Function<Object, T> func) {
for (int i = 0; i < instrNumber; i++) {
Instruction code = instructions[i];
T res = func.apply(code);
if (res != null) {
return res;
}
for (Object param : code.getParameters()) {
res = func.apply(param);
if (res != null) {
return res;
}
if (param instanceof Program) {
res = ((Program) param).itterate(func);
}
if (res != null) {
return res;
}
}
}
return null;
}
public ProgramBuilder addAll(final Instruction[] objects, final List<Location> debug) {
ensureCapacity(instrNumber + objects.length);
System.arraycopy(objects, 0, instructions, instrNumber, objects.length);
instrNumber += objects.length;
this.debugInfo.addAll(debug);
return this;
}
public ProgramBuilder addAll(final ProgramBuilder opb) {
ensureCapacity(instrNumber + opb.instrNumber);
System.arraycopy(opb.instructions, 0, instructions, instrNumber, opb.instrNumber);
instrNumber += opb.instrNumber;
this.debugInfo.addAll(opb.debugInfo);
return this;
}
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if necessary, to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void ensureCapacity(final int minCapacity) {
int oldCapacity = instructions.length;
if (minCapacity > oldCapacity) {
int newCapacity = (oldCapacity * 3) / 2 + 1;
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
instructions = Arrays.copyOf(instructions, newCapacity);
}
}
public int size() {
return instrNumber;
}
public Object[] toArray() {
return Arrays.copyOf(instructions, instrNumber);
}
public boolean hasDeterministicFunctions() {
Boolean hasDetFuncs = itterate(new HasDeterministicFunc());
if (hasDetFuncs == null) {
hasDetFuncs = Boolean.FALSE;
}
return hasDetFuncs;
}
public boolean hasAsyncCalls() {
Boolean hasAsyncCalls = itterate(new HasAsyncFunc());
if (hasAsyncCalls == null) {
return false;
}
return hasAsyncCalls;
}
public Program toProgram(final String name, final String source, final String... parameterNames)
throws CompileException {
return toProgram(name, source, parameterNames, Collections.EMPTY_MAP);
}
public Program toProgram(final String name, final String source, final String[] parameterNames,
final Map<String, Integer> localTable)
throws CompileException {
intern(instructions);
intern(parameterNames);
Pair<Object[], Map<String, Integer>> build = staticMemBuilder.build();
boolean hasAsyncPrograms = false;
for (Object obj : build.getFirst()) {
if (obj instanceof Program) {
Program prog = (Program) obj;
if (prog.getExecType() == Program.ExecutionType.ASYNC) {
hasAsyncPrograms = true;
break;
}
}
}
return new Program(name, build.getSecond(), build.getFirst(), localTable, instructions,
debugInfo.toArray(new Location[debugInfo.size()]), source, 0, instrNumber, type,
hasAsyncPrograms || this.execType == Program.ExecutionType.ASYNC || hasAsyncCalls()
? (this.execType == null ? Program.ExecutionType.ASYNC : this.execType)
: Program.ExecutionType.SYNC,
hasDeterministicFunctions(), parameterNames);
}
public Program toProgram(final String name, final String source, final List<String> parameterNames)
throws CompileException {
return toProgram(name, source, parameterNames.toArray(new String[parameterNames.size()]));
}
private static final class HasDeterministicFunc implements Function<Object, Boolean> {
@Override
@SuppressFBWarnings({ "TBP_TRISTATE_BOOLEAN_PATTERN", "NP_BOOLEAN_RETURN_NULL" })
@Nullable
public Boolean apply(final Object input) {
if (input instanceof Program) {
Program prog = (Program) input;
if (prog.getType() == Program.Type.DETERMINISTIC
|| prog.hasDeterministicFunctions()) {
return Boolean.TRUE;
}
}
return null;
}
}
private static final class HasAsyncFunc implements Function<Object, Boolean> {
@Override
@SuppressFBWarnings({ "TBP_TRISTATE_BOOLEAN_PATTERN",
"ITC_INHERITANCE_TYPE_CHECKING", "NP_BOOLEAN_RETURN_NULL" })
@Nullable
public Boolean apply(final Object input) {
if (input instanceof Program) {
Program prog = (Program) input;
if (prog.getExecType() == Program.ExecutionType.ASYNC) {
return Boolean.TRUE;
}
} else if (input instanceof CALLA) {
return Boolean.TRUE;
}
return null;
}
}
@Override
public String toString() {
return "ProgramBuilder{" + "instructions=" + Arrays.toString(instructions) + ", debugInfo=" + debugInfo
+ ", instrNumber=" + instrNumber + ", type=" + type + ", execType=" + execType
+ ", stringInterner=" + stringInterner + ", staticMemBuilder=" + staticMemBuilder + '}';
}
}