source: trunk/Compiler/ModelicaFlatTree/src/jastadd/ConstantEvaluation/ExternalConstantEvaluation.jrag @ 11541

Last change on this file since 11541 was 11541, checked in by Jonathan Kämpe, 15 months ago

#5670 Fixed a bug where external constant evaluation with literal or size inputs could produce wrong values if several modelica wrappers used the same external function.

File size: 13.2 KB
Line 
1/*
2    Copyright (C) 2009-2017 Modelon AB
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, version 3 of the License.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.
15*/
16
17
18import java.io.BufferedReader;
19import java.io.BufferedWriter;
20import java.io.FileNotFoundException;
21import java.io.InputStreamReader;
22import java.io.IOException;
23import java.io.OutputStreamWriter;
24import java.util.Map;
25import java.util.Timer;
26import java.util.TimerTask;
27
28import org.jmodelica.common.evaluation.ExternalFunction;
29import org.jmodelica.common.evaluation.ExternalProcessCache;
30import org.jmodelica.common.evaluation.ExternalProcessCacheImpl;
31import org.jmodelica.common.evaluation.ExternalProcessMultiCache;
32import org.jmodelica.common.evaluation.ProcessCommunicator;
33
34aspect ExternalConstantEvaluation {
35   
36    public interface ExternalArgument {
37        CValue ceval();
38        FType type();
39       
40        String name_C();
41        boolean isOutput();
42    }
43   
44    public interface CommonVariableDecl extends ExternalArgument {}
45    FExp implements ExternalArgument;
46   
47    syn boolean FExp.isOutput() = false;
48   
49    class ModelicaCompiler {}
50   
51    ModelicaCompiler   implements ExternalProcessMultiCache.Compiler<ExternalArgument, FExternalStmt>;
52    FExternalStmt      implements ExternalProcessMultiCache.External<ExternalArgument>;
53    ExternalArgument   extends    ExternalProcessMultiCache.Variable<CValue,FType>;
54    CValue             implements ExternalProcessMultiCache.Value;
55    FType              implements ExternalProcessMultiCache.Type<CValue>;
56   
57    public class ExternalFunctionCache extends ExternalProcessMultiCache<ExternalArgument, CValue, FType, FExternalStmt> {
58        public ExternalFunctionCache(ModelicaCompiler mc) {
59            super(mc);
60        }
61    }
62   
63    /**
64     * Check if this external function can be evaluated. Throw ConstantEvaluationException otherwise
65     */
66    public void FExternalStmt.checkCanEvaluate(AlgorithmEvaluator evaluator, Map<ExternalArgument, CValue> values) throws ConstantEvaluationException {
67        if (evaluator.externalEvaluation() == 0) {
68            throw new ConstantEvaluationException(null, "Could not evaluate external function, external evaluation disabled");
69        }
70        for (FExp arg : getArgs()) {
71            if (!arg.type().externalValid()) {
72                throw new ConstantEvaluationException(null, "Could not evaluate external function, invalid argument type");
73            }
74        }
75        if (hasReturnVar() && !getReturnVar().type().externalValid()) {
76            throw new ConstantEvaluationException(null, "Could not evaluate external function, invalid return type");
77        }
78        Collection<ExternalArgument> outputs = varsToDeserialize();
79        for (ExternalArgument arg : values.keySet()) {
80            if (!outputs.contains(arg)) {
81                if (values.get(arg).isPartlyUnknown()) {
82                    throw new ConstantEvaluationException(null, "Could not evaluate external function, unknown values in arguments");
83                }
84            }
85        }
86    }
87
88    /**
89     * Check if this external function should be cached as a live process.
90     */
91    syn boolean FExternalStmt.shouldCacheProcess() {
92        return myOptions().getIntegerOption("external_constant_evaluation_max_proc") > 0;
93    }
94   
95    /**
96     * Returns a single scalar external object which can be cached. If there is not exactly
97     * one scalar external object, return null.
98     */
99    syn ExternalArgument FExternalStmt.cachedExternalObject() {
100        ExternalArgument eo = null;
101        for (ExternalArgument cvd : varsToSerialize()) {
102            if (cvd.type().isExternalObject() && cvd.type().isScalar()) {
103                if (eo != null) {
104                    return null;
105                } else {
106                    eo = cvd;
107                }
108            }
109        }
110        return eo;
111    }
112   
113    /**
114     * Mark external object CValue with name of external object. Used to track origin of CValue.
115     */
116    public void CValue.markExternalObject(String name) {
117       
118    }
119   
120    private String CValueExternalObject.marked = null;
121    public void CValueExternalObject.markExternalObject(String name) {
122        if (marked == null) {
123            marked = name;
124        }
125    }
126   
127    /**
128     * Get name of external object instance which this CValue represents.
129     */
130    public String CValue.getMarkedExternalObject() {
131        throw new ConstantEvaluationException();
132    }
133   
134    public String CValueExternalObject.getMarkedExternalObject() {
135        if (marked == null) {
136            return super.getMarkedExternalObject();
137        }
138        return marked;
139    }
140   
141    /**
142     * Evaluate this statement as an external function constructor call. Stores evaluated
143     * inputs in a CValueExternalObject.
144     */
145    public int FExternalStmt.evaluateConstructor(Map<CommonVariableDecl, CValue> values) {
146        ArrayList<FExp> args = myConstructorArgs();
147        CValue[] vals = new CValue[args.size()];
148        for (int i = 0; i < args.size(); i++)
149            vals[i] = args.get(i).ceval();
150        values.put(myConstructorOutput(), new CValueExternalObject(vals));
151        return EVAL_CONT;
152    }
153   
154    inh boolean FExternalStmt.isConstructorStmt();
155    eq Root.getChild().isConstructorStmt() = false;
156    eq FFunctionDecl.getChild().isConstructorStmt() = isConstructor();
157    eq InstClassDecl.getChild().isConstructorStmt() = isConstructor();
158   
159    inh boolean FExternalStmt.isDestructorStmt();
160    eq Root.getChild().isDestructorStmt() = false;
161    eq FFunctionDecl.getChild().isDestructorStmt() = isDestructor();
162    eq InstClassDecl.getChild().isDestructorStmt() = isDestructor();
163   
164    /**
165     * Retrieve {@link ExternalFunction} object which represents the external function
166     * this statement refers to.
167     */
168    public ExternalFunction<ExternalArgument, CValue> FExternalStmt.myExternalFunction() {
169        ExternalFunctionCache efc = root().getUtilInterface().getExternalFunctionCache();
170        if (efc == null) {
171            return new ExternalProcessCacheImpl<>(root().getUtilInterface().getModelicaCompiler())
172                .failedEval(this, "external function cache unavailable", false);
173        }
174        return efc.getExternalProcessCache(getLibTopPackagePath()).getExternalFunction(this);
175    }
176   
177    /**
178     * Evaluate this external statement.
179     */
180    public int FExternalStmt.evaluateExternal(AlgorithmEvaluator evaluator) {
181       
182        Map<ExternalArgument, CValue> values = new LinkedHashMap<>();
183        for (ExternalArgument arg : varsToSerialize()) {
184            values.put(arg, arg.ceval());
185        }
186       
187        checkCanEvaluate(evaluator, values);
188       
189        if (isConstructorStmt()) {
190            return evaluateConstructor(evaluator.getValues());
191        }
192       
193        int res = 0;
194        int timeout = evaluator.externalEvaluation();
195        ExternalFunction<ExternalArgument,CValue> ef = myExternalFunction();
196        String error = null;
197        try {
198            res = ef.evaluate(this, values, timeout);
199            if (res != 0) {
200                error = "process returned '" + res + "'";
201            }
202        } catch (IOException e) {
203            error = "error in process communication: '"+ e.getMessage() + "'";
204        }
205       
206        if (error != null) {
207            throw new ConstantEvaluationException(null, ExternalProcessCacheImpl.failedEvalMsg(getName(), error));
208        }
209       
210        for (ExternalArgument output : varsToDeserialize()) {
211            evaluator.getValues().put((CommonVariableDecl)output, values.get(output));
212        }
213       
214        return EVAL_CONT;
215    }
216   
217    public String ModelicaCompiler.compileExternal(FExternalStmt ext) throws FileNotFoundException, CcodeCompilationException {
218        String executable = null;
219        if (outDir == null)
220            setRandomOutDir();
221        String source = ext.getName().replace(".", "_");
222        TargetObject target = createTargetObject("ceval", "0.1");
223        Set<String> incDirs = new LinkedHashSet<String>();
224        Set<String> libs    = new LinkedHashSet<String>();
225        Set<String> libDirs = new LinkedHashSet<String>();
226       
227        ext.externalDependencies(null, incDirs, libs, libDirs);
228       
229        OptionRegistry options = ext.myOptions();
230        ModulesSettings modulesSettings = createModulesSettings(options);
231       
232        target.getTemplates(options).generateCFiles(ModelicaCompiler.this, null, createCGenerator(ext), sourceDir, source);
233       
234        CCompilerDelegator ccompiler = getCCompiler();
235        ccompiler.setModuleLibraryNames(modulesSettings.getLibraryNames());
236       
237        CCompilerArguments ccArgs = new CCompilerArguments(source, options, target,
238                libs, libDirs, incDirs);
239        executable = ccompiler.compileCCodeLocal(ModelicaCompiler.log, ccArgs, outDir);
240        new File(sourceDir, source + ".c").delete();
241        return executable;
242    }
243}
244
245aspect ExternalConstantEvaluationCaching {
246   
247    protected ExternalFunctionCache ModelicaCompiler.externalFunctionCache = new ExternalFunctionCache(this);
248   
249    public ExternalFunctionCache ModelicaCompiler.getExternalFunctionCache() {
250        return externalFunctionCache;
251    }
252}
253
254aspect ExternalProcessCommunication {
255   
256    /**
257     * Print this constant value to <code>buff</code>
258     */
259    public void CValue.serialize(BufferedWriter buff) throws IOException {
260        throw new IOException("Unsupported type to serialize '" + getClass().getSimpleName() + "'");
261    }
262   
263    public void CValueUnknown.serialize(BufferedWriter buff) throws IOException {
264        throw new IOException("Uninitialized value when expecting initialized");
265    }
266    public void CValueArray.serialize(BufferedWriter buff) throws IOException {
267        for (int s : size().size) {
268            buff.write("" + s + "\n");
269        }
270        for (Index i : indices()) {
271            getCell(i).serialize(buff);
272        }
273    }
274    public void CValueRecord.serialize(BufferedWriter buff) throws IOException {
275        for (CValue value : values) {
276            value.serialize(buff);
277        }
278    }
279    public void CValueReal.serialize(BufferedWriter buff) throws IOException {
280        buff.write(Double.toString(realValue()));
281        buff.write("\n");
282    }
283    public void CValueInteger.serialize(BufferedWriter buff) throws IOException {
284        buff.write(Integer.toString(intValue()));
285        buff.write("\n");
286    }
287    public void CValueBoolean.serialize(BufferedWriter buff) throws IOException {
288        buff.write(booleanValue() ? "1\n" : "0\n");
289    }
290    public void CValueString.serialize(BufferedWriter buff) throws IOException {
291        String s = stringValue();
292        buff.write("" + s.length() + " ");
293        buff.write(s);
294        buff.write("\n");
295    }
296    public void CValueEnum.serialize(BufferedWriter buff) throws IOException {
297        buff.write(Integer.toString(intValue()));
298        buff.write("\n");
299    }
300    public void CValueExternalObject.serialize(BufferedWriter buff) throws IOException {
301        for (CValue v : values) {
302            v.serialize(buff);
303        }
304    }
305   
306   
307    /**
308     * Read a constant value of <code>this</code> type from <code>buff</code>
309     */
310    public CValue FType.deserialize(ProcessCommunicator com) throws IOException {
311        if (isArray()) {
312            CValueArray a = new CValueArray(size().ceval());
313            for (Index i : a.indices()) {
314                a.setCell(i, deserializeScalar(com));
315            }
316            return a;
317        } else {
318            return deserializeScalar(com);
319        }
320    }
321    public CValue FType.deserializeScalar(ProcessCommunicator com) throws IOException {
322        throw new IOException("Unsupported type to deserialize '" + getClass().getSimpleName() + "'");
323    }
324    public CValue FRecordType.deserializeScalar(ProcessCommunicator com) throws IOException {
325        CValueRecord res = new CValueRecord(this);
326        for (FRecordComponentType frct : getComponents()) {
327            res.setMember(frct.getName(), frct.getFType().deserialize(com));
328        }
329        return res;
330    }
331    public CValue FRealType.deserializeScalar(ProcessCommunicator com) throws IOException {
332        return new CValueReal(com.deserializeReal());
333    }
334    public CValue FIntegerType.deserializeScalar(ProcessCommunicator com) throws IOException {
335        return new CValueInteger((int) com.deserializeReal());
336    }
337    public CValue FBooleanType.deserializeScalar(ProcessCommunicator com) throws IOException {
338        return new CValueBoolean(com.deserializeReal() != 0);
339    }
340    public CValue FStringType.deserializeScalar(ProcessCommunicator com) throws IOException {
341        return new CValueString(com.deserializeString());
342    }
343    public CValue FEnumType.deserializeScalar(ProcessCommunicator com) throws IOException {
344        return new CValueEnum(this, (int) com.deserializeReal());
345    }
346}
347
348
Note: See TracBrowser for help on using the repository browser.