]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java
Showing compilation warnings in SCL issue view and editors
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / top / ExpressionEvaluator.java
1 package org.simantics.scl.compiler.top;
2
3 import java.io.StringReader;
4 import java.lang.reflect.Method;
5 import java.util.ArrayList;
6 import java.util.ListIterator;
7 import java.util.Map;
8
9 import org.simantics.scl.compiler.common.names.Name;
10 import org.simantics.scl.compiler.compilation.CodeGeneration;
11 import org.simantics.scl.compiler.compilation.CompilationContext;
12 import org.simantics.scl.compiler.constants.JavaStaticMethod;
13 import org.simantics.scl.compiler.constants.SCLConstant;
14 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
15 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
16 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
17 import org.simantics.scl.compiler.elaboration.errors.NotPatternException;
18 import org.simantics.scl.compiler.elaboration.expressions.EApply;
19 import org.simantics.scl.compiler.elaboration.expressions.EBlock;
20 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
21 import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant;
22 import org.simantics.scl.compiler.elaboration.expressions.EVar;
23 import org.simantics.scl.compiler.elaboration.expressions.Expression;
24 import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement;
25 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
26 import org.simantics.scl.compiler.elaboration.expressions.block.Statement;
27 import org.simantics.scl.compiler.elaboration.java.Builtins;
28 import org.simantics.scl.compiler.environment.Environment;
29 import org.simantics.scl.compiler.environment.LocalEnvironment;
30 import org.simantics.scl.compiler.errors.ErrorLog;
31 import org.simantics.scl.compiler.internal.codegen.references.IVal;
32 import org.simantics.scl.compiler.internal.codegen.ssa.SSAModule;
33 import org.simantics.scl.compiler.internal.codegen.types.DummyJavaReferenceValidator;
34 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
35 import org.simantics.scl.compiler.internal.codegen.utils.CodeBuildingException;
36 import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
37 import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;
38 import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
39 import org.simantics.scl.compiler.internal.codegen.utils.ValueFromMethod;
40 import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter;
41 import org.simantics.scl.compiler.internal.codegen.writer.ExternalConstant;
42 import org.simantics.scl.compiler.internal.codegen.writer.ModuleWriter;
43 import org.simantics.scl.compiler.internal.elaboration.decomposed.DecomposedExpression;
44 import org.simantics.scl.compiler.internal.elaboration.utils.ExpressionDecorator;
45 import org.simantics.scl.compiler.internal.interpreted.IExpression;
46 import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;
47 import org.simantics.scl.compiler.internal.parsing.parser.SCLBlockParser;
48 import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
49 import org.simantics.scl.compiler.internal.parsing.parser.SCLParserOptions;
50 import org.simantics.scl.compiler.runtime.MutableClassLoader;
51 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
52 import org.simantics.scl.compiler.types.TVar;
53 import org.simantics.scl.compiler.types.Type;
54 import org.simantics.scl.compiler.types.Types;
55 import org.simantics.scl.compiler.types.kinds.Kinds;
56 import org.simantics.scl.compiler.types.util.Polarity;
57 import org.simantics.scl.compiler.types.util.ProcedureType;
58 import org.simantics.scl.runtime.function.FunctionImpl1;
59 import org.simantics.scl.runtime.tuple.Tuple0;
60
61 import gnu.trove.set.hash.THashSet;
62
63 public class ExpressionEvaluator {
64
65     public static final boolean TRACE_INTERPRETATION_VS_COMPILATION = false;
66     private static final String COMPUTATION_METHOD_NAME = "main";
67     
68     private final RuntimeEnvironment runtimeEnvironment;
69     private final String expressionText;
70     private Expression expression;
71     private Type expressionType;
72     
73     private Type expectedEffect;
74     private boolean decorateExpression;
75     private Type expectedType;
76     private LocalEnvironment localEnvironment;
77     private LocalStorage localStorage;
78     private boolean interpretIfPossible = true;
79     private ExpressionParseMode parseMode = ExpressionParseMode.EXPRESSION;
80     
81     public ExpressionEvaluator(RuntimeEnvironment runtimeEnvironment,
82             String expressionText) {
83         if(runtimeEnvironment == null)
84             throw new NullPointerException();
85         if(expressionText == null)
86             throw new NullPointerException();
87         this.runtimeEnvironment = runtimeEnvironment;
88         this.expressionText = expressionText;
89     }
90     
91     public ExpressionEvaluator(RuntimeEnvironment runtimeEnvironment,
92             LocalStorage localStorage, Expression expression) {
93         if(runtimeEnvironment == null)
94             throw new NullPointerException();
95         if(expression == null)
96             throw new NullPointerException();
97         this.runtimeEnvironment = runtimeEnvironment;
98         this.localStorage = localStorage;
99         this.expressionText = null;
100         this.expression = expression;
101     }
102     
103     public ExpressionEvaluator expectedEffect(Type expectedEffect) {
104         this.expectedEffect = expectedEffect;
105         return this;
106     }
107     
108     public ExpressionEvaluator decorateExpression(boolean decorateExpression) {
109         this.decorateExpression = decorateExpression;
110         return this;
111     }
112     
113     public ExpressionEvaluator expectedType(Type expectedType) {
114         this.expectedType = expectedType;
115         return this;
116     }
117     
118     /**
119      * Sets a local environment that can arbitrarily modify the resolving of the expression.
120      */
121     public ExpressionEvaluator localEnvironment(LocalEnvironment localEnvironment) {
122         this.localEnvironment = localEnvironment;
123         return this;
124     }
125     
126     /**
127      * Evaluates the expression by interpretation instead of compilation to bytecode
128      * if the expression does not contain language constructs that interpretation does
129      * not support.
130      */
131     public ExpressionEvaluator interpretIfPossible(boolean interpretIfPossible) {
132         this.interpretIfPossible = interpretIfPossible;
133         return this;
134     }
135     
136     /**
137      * Assumes that top level of the expression is similar to the content
138      * of a do-block.
139      */
140     public ExpressionEvaluator parseAsBlock(boolean parseAsBlock) {
141         this.parseMode = parseAsBlock ? ExpressionParseMode.BLOCK : ExpressionParseMode.EXPRESSION;
142         return this;
143     }
144     
145     public ExpressionEvaluator parseModel(ExpressionParseMode parseMode) {
146         this.parseMode = parseMode;
147         return this;
148     }
149     
150     private void fillDefaults() {
151         if(expectedEffect == null)
152             expectedEffect = Types.metaVar(Kinds.EFFECT);
153         if(expectedType == null)
154             expectedType = Types.metaVar(Kinds.STAR);
155     }
156     
157     private static class StoreFunction extends FunctionImpl1<Object, Object> {
158         final LocalStorage storage;
159         final String name;
160         final Type type;
161         public StoreFunction(LocalStorage storage, String name, Type type) {
162             this.storage = storage;
163             this.name = name;
164             this.type = type;
165         }
166         @Override
167         public Object apply(Object value) {
168             Type type = Types.closure(this.type.convertMetaVarsToVars());
169             storage.store(name, value, type);
170             return Tuple0.INSTANCE;
171         }
172         
173         @Override
174         public String toString() {
175             return "store_" + name;
176         }
177     }
178
179     public Object eval() throws SCLExpressionCompilationException {
180         fillDefaults();
181         
182         final CompilationContext compilationContext = new CompilationContext();
183         final ErrorLog errorLog = compilationContext.errorLog;
184         final Environment environment = runtimeEnvironment.getEnvironment();
185         compilationContext.environment = environment;
186         
187         // Parse expression
188         if(expressionText != null) {
189             try {
190                 switch(parseMode) {
191                 case BLOCK: {
192                     SCLBlockParser parser = new SCLBlockParser(new StringReader(expressionText));
193                     parser.parseCommands();
194                     expression = parser.block;
195                 } break;
196                 case EXPRESSION: {
197                     SCLParserImpl parser = new SCLParserImpl(new StringReader(expressionText));
198                     expression = (Expression)parser.parseExp();
199                 } break;
200                 case EQUATION_BLOCK: {
201                     SCLParserImpl parser = new SCLParserImpl(new StringReader(expressionText));
202                     SCLParserOptions parserOptions = new SCLParserOptions();
203                     parserOptions.supportEq = true;
204                     parser.setParserOptions(parserOptions);
205                     expression = (Expression)parser.parseEquationBlock();
206                 } break;
207                 }
208             } catch(SCLSyntaxErrorException e) {
209                 errorLog.log(e.location, e.getMessage());
210                 //System.out.println(errorLog.getErrorsAsString());
211                 throw new SCLExpressionCompilationException(errorLog.getErrors());
212             } catch(Exception e) {
213                 errorLog.log(e);
214                 throw new SCLExpressionCompilationException(errorLog.getErrors());
215             }
216         }
217         
218         // Store local variables
219         ArrayList<Type> lvTypes = new ArrayList<Type>(); 
220         if(expression instanceof EBlock) {
221             EBlock block = (EBlock)expression;
222             if(localStorage != null && !(block.getStatements().getLast() instanceof GuardStatement)) {
223                 THashSet<String> localVariables = new THashSet<String>();
224                 ListIterator<Statement> it = block.getStatements().listIterator();
225                 while(it.hasNext()) {
226                     Statement stat = it.next();
227                     if(!(stat instanceof LetStatement))
228                         continue;
229                     String variableName;
230                     try {
231                         variableName = ((LetStatement)stat).pattern.getPatternHead().name;
232                     } catch (NotPatternException e) {
233                         continue;
234                     }
235                     localVariables.add(variableName);
236                 }
237                 for(String variableName : localVariables) {
238                     Type type = Types.metaVar(Kinds.STAR);
239                     lvTypes.add(type);
240                     block.addStatement(new GuardStatement(new EApply(
241                             new EExternalConstant(
242                                     new StoreFunction(localStorage, variableName, type),
243                                     Types.functionE(type, Types.PROC, Types.UNIT)),
244                                     new EVar(variableName)
245                             )));
246                 }
247             }
248             if(!(block.getStatements().getLast() instanceof GuardStatement))
249                 block.addStatement(new GuardStatement(new EConstant(Builtins.TUPLE_CONSTRUCTORS[0])));
250         }
251         
252         // Elaboration
253         {
254             TranslationContext context = new TranslationContext(compilationContext, localEnvironment);
255             expression = expression.resolve(context);
256             if(!errorLog.hasNoErrors())
257                 throw new SCLExpressionCompilationException(errorLog.getErrors());
258         }
259         
260         // Apply local environment
261         if(localEnvironment != null) {
262             expression = localEnvironment.preDecorateExpression(expression);
263             ProcedureType procedureType = localEnvironment.decorateExpectedType(expectedType, expectedEffect);
264             expectedType = procedureType.type;
265             expectedEffect = procedureType.effect;
266         }
267         
268         // Type checking
269         {
270             TypingContext context = new TypingContext(compilationContext);
271
272             context.pushEffectUpperBound(expression.location, expectedEffect);
273             expression = expression.checkType(context, expectedType);
274             context.popEffectUpperBound();
275
276             for(Type lvType : lvTypes)
277                 lvType.addPolarity(Polarity.POSITIVE);
278             
279             expectedType.addPolarity(Polarity.POSITIVE);
280             context.solveSubsumptions(expression.location);
281             if(!errorLog.hasNoErrors())
282                 throw new SCLExpressionCompilationException(errorLog.getErrors());
283             if(decorateExpression && Types.canonical(expectedEffect) != Types.NO_EFFECTS) {
284                 ExpressionDecorator decorator =
285                         new ToplevelEffectDecorator(errorLog, environment);
286                 expression = expression.decorate(decorator);
287             }
288             expression = context.solveConstraints(environment, expression);
289             expressionType = expression.getType();
290             
291             if(!errorLog.hasNoErrors())
292                 throw new SCLExpressionCompilationException(errorLog.getErrors());
293
294             if(localEnvironment != null)
295                 expression = localEnvironment.postDecorateExpression(expression);
296
297             Type type = expression.getType();
298             type = type.convertMetaVarsToVars();
299             
300             for(Type lvType : lvTypes)
301                 lvType.convertMetaVarsToVars();
302             
303             ArrayList<TVar> varsList = Types.freeVars(type);
304             expression = expression.closure(varsList.toArray(new TVar[varsList.size()]));
305         }
306         
307         // Initialize code generation
308         MutableClassLoader classLoader = runtimeEnvironment.getMutableClassLoader();
309         String moduleName = classLoader.getFreshPackageName();
310         JavaTypeTranslator javaTypeTranslator = new JavaTypeTranslator(environment);
311         compilationContext.javaTypeTranslator = javaTypeTranslator;
312         JavaNamingPolicy namingPolicy = new JavaNamingPolicy(moduleName);
313         compilationContext.namingPolicy = namingPolicy;
314
315         ModuleBuilder moduleBuilder = new ModuleBuilder(namingPolicy, javaTypeTranslator);
316         
317         // Simplify
318         SimplificationContext context = 
319                 new SimplificationContext(compilationContext, DummyJavaReferenceValidator.INSTANCE);
320         expression = expression.simplify(context);
321         
322         if(!errorLog.hasNoErrors())
323             throw new SCLExpressionCompilationException(errorLog.getErrors());
324         
325         if(SCLCompilerConfiguration.SHOW_EXPRESSION_BEFORE_EVALUATION)
326             System.out.println(expression);
327         
328         if(interpretIfPossible) {
329         // Try to interpret
330         try {
331             ExpressionInterpretationContext expressionInterpretationContext =
332                     new ExpressionInterpretationContext(runtimeEnvironment, 
333                             new TransientClassBuilder(classLoader, javaTypeTranslator));
334             IExpression iexp = expression.toIExpression(expressionInterpretationContext);
335                 if(TRACE_INTERPRETATION_VS_COMPILATION)
336                 System.out.println("INTERPRETED " + expressionText);
337                 if(SCLCompilerConfiguration.SHOW_INTERPRETED_EXPRESSION)
338                     System.out.println("INTERPRETED AS: " + iexp);
339             return iexp.execute(new Object[expressionInterpretationContext.getMaxVariableId()]);
340         } catch(UnsupportedOperationException e) {
341             // This is normal when expression cannot be interpreted. We compile it instead.
342         }
343         }
344         
345         // Convert to SSA
346         ModuleWriter mw = new ModuleWriter(namingPolicy.getModuleClassName());
347         DecomposedExpression decomposed = 
348                 DecomposedExpression.decompose(expression);
349
350         SCLConstant constant = new SCLConstant(
351                 Name.create(moduleName, COMPUTATION_METHOD_NAME),
352                 expression.getType());
353         constant.setBase(new JavaStaticMethod(
354                 moduleName, COMPUTATION_METHOD_NAME,
355                 decomposed.effect,
356                 decomposed.typeParameters,
357                 decomposed.returnType, 
358                 decomposed.parameterTypes));
359         try {
360             CodeWriter w = mw.createFunction(constant,
361                     decomposed.typeParameters,
362                     decomposed.effect,
363                     decomposed.returnType, 
364                     decomposed.parameterTypes);
365             constant.setDefinition(w.getFunction());
366             IVal[] parameterVals = w.getParameters();
367             for(int i=0;i<decomposed.parameters.length;++i)
368                 decomposed.parameters[i].setVal(parameterVals[i]);
369             w.return_(decomposed.body.toVal(environment, w));
370         } catch(RuntimeException e) {
371             errorLog.setExceptionPosition(expression.location);
372             throw new SCLExpressionCompilationException(errorLog.getErrors());
373         }
374
375         SSAModule ssaModule = mw.getModule();
376         if(SCLCompilerConfiguration.SHOW_SSA_BEFORE_OPTIMIZATION) {
377             System.out.println("=== SSA before optimization ==================================");
378             System.out.println(ssaModule);            
379         }
380         if(SCLCompilerConfiguration.DEBUG)
381             ssaModule.validate();
382
383         ExternalConstant[] externalConstants = mw.getExternalConstants();
384         
385         // Optimize SSA
386         for(int phase=0;phase<CodeGeneration.OPTIMIZATION_PHASES;++phase) {
387             int optCount = 0;
388             while(optCount++ < 4 && ssaModule.simplify(environment, phase)) {
389                 //System.out.println("simplify " + optCount);
390             }
391         }
392         if(SCLCompilerConfiguration.SHOW_SSA_BEFORE_LAMBDA_LIFTING) {
393             System.out.println("=== SSA before lambda lifting ==================================");
394             System.out.println(ssaModule);            
395         }
396         //ssaModule.saveInlinableDefinitions();
397         ssaModule.lambdaLift(errorLog);
398         //ssaModule.validate();
399         ssaModule.markGenerateOnFly();
400         
401         // Generate code
402         if(SCLCompilerConfiguration.SHOW_FINAL_SSA)
403             System.out.println(ssaModule);
404         try {
405             ssaModule.generateCode(moduleBuilder);
406         } catch (CodeBuildingException e) {
407             errorLog.log(e);
408             throw new SCLExpressionCompilationException(errorLog.getErrors());
409         }
410         Map<String, byte[]> classes = moduleBuilder.getClasses();
411         
412         // Load generated code and execute
413         try {
414             classLoader.addClasses(classes);
415             Class<?> clazz = classLoader.loadClass(MutableClassLoader.SCL_PACKAGE_PREFIX + moduleName);
416             for(ExternalConstant externalConstant : externalConstants)
417                 clazz.getField(externalConstant.fieldName).set(null, externalConstant.value);
418             for(Method method : clazz.getMethods()) {
419                 if(method.getName().equals(COMPUTATION_METHOD_NAME))
420                     return ValueFromMethod.getValueFromStaticMethod(method);
421             }
422             errorLog.log("Internal compiler error: didn't find method " +
423                     COMPUTATION_METHOD_NAME + " from generated byte code.");
424             throw new SCLExpressionCompilationException(errorLog.getErrors());
425         } catch(ReflectiveOperationException e) {
426             errorLog.log(e);
427             throw new SCLExpressionCompilationException(errorLog.getErrors());
428         }
429     }
430
431     public Type getType() {
432         return expressionType;
433     }
434
435     public String getExpressionText() {
436         return expressionText;
437     }
438     
439 }