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