/******************************************************************************* * * Copyright (C) 2008 Fujitsu Services Ltd. * * Author: Nick Battle * * This file is part of VDMJ. * * VDMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VDMJ 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 General Public License * along with VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.interpreter.runtime; import java.io.File; import java.util.List; import java.util.Set; import java.util.Vector; import org.overture.ast.analysis.AnalysisException; import org.overture.ast.definitions.ANamedTraceDefinition; import org.overture.ast.definitions.ATypeDefinition; import org.overture.ast.definitions.PDefinition; import org.overture.ast.definitions.SClassDefinition; import org.overture.ast.expressions.PExp; import org.overture.ast.factory.AstFactory; import org.overture.ast.intf.lex.ILexLocation; import org.overture.ast.lex.Dialect; import org.overture.ast.lex.LexLocation; import org.overture.ast.lex.LexNameToken; import org.overture.ast.lex.LexToken; import org.overture.ast.lex.VDMToken; import org.overture.ast.statements.PStm; import org.overture.ast.typechecker.NameScope; import org.overture.ast.types.PType; import org.overture.ast.util.Utils; import org.overture.ast.util.definitions.ClassList; import org.overture.config.Settings; import org.overture.interpreter.assistant.IInterpreterAssistantFactory; import org.overture.interpreter.assistant.InterpreterAssistantFactory; import org.overture.interpreter.debug.DBGPReader; import org.overture.interpreter.messages.Console; import org.overture.interpreter.messages.rtlog.RTLogger; import org.overture.interpreter.messages.rtlog.RTThreadCreateMessage; import org.overture.interpreter.messages.rtlog.RTThreadKillMessage; import org.overture.interpreter.messages.rtlog.RTThreadSwapMessage; import org.overture.interpreter.messages.rtlog.RTThreadSwapMessage.SwapType; import org.overture.interpreter.scheduler.BasicSchedulableThread; import org.overture.interpreter.scheduler.CTMainThread; import org.overture.interpreter.scheduler.ISchedulableThread; import org.overture.interpreter.scheduler.InitThread; import org.overture.interpreter.scheduler.MainThread; import org.overture.interpreter.scheduler.RunState; import org.overture.interpreter.scheduler.Signal; import org.overture.interpreter.scheduler.SystemClock; import org.overture.interpreter.traces.CallSequence; import org.overture.interpreter.util.ClassListInterpreter; import org.overture.interpreter.values.BUSValue; import org.overture.interpreter.values.CPUValue; import org.overture.interpreter.values.NameValuePair; import org.overture.interpreter.values.NameValuePairList; import org.overture.interpreter.values.NameValuePairMap; import org.overture.interpreter.values.ObjectValue; import org.overture.interpreter.values.Value; import org.overture.parser.lex.LexTokenReader; import org.overture.parser.messages.VDMErrorsException; import org.overture.parser.syntax.ExpressionReader; import org.overture.parser.syntax.ParserException; import org.overture.pog.obligation.ProofObligationList; import org.overture.typechecker.Environment; import org.overture.typechecker.FlatCheckedEnvironment; import org.overture.typechecker.PrivateClassEnvironment; import org.overture.typechecker.PublicClassEnvironment; import org.overture.typechecker.assistant.definition.PDefinitionSet; /** * The VDM++ interpreter. */ public class ClassInterpreter extends Interpreter { private final ClassListInterpreter classes; private SClassDefinition defaultClass; private NameValuePairMap createdValues; private PDefinitionSet createdDefinitions; public ClassInterpreter(ClassList classes) throws Exception { this(new InterpreterAssistantFactory(), classes); } public ClassInterpreter(IInterpreterAssistantFactory assistantFactory, ClassList classes) throws Exception { super(assistantFactory); this.classes = new ClassListInterpreter(classes); this.createdValues = new NameValuePairMap(); this.createdDefinitions = assistantFactory.createPDefinitionSet(); if (classes.isEmpty()) { setDefaultName(null); } else { setDefaultName(classes.get(0).getName().getName()); } } @Override public void setDefaultName(String cname) throws Exception { if (cname == null) { defaultClass = AstFactory.newAClassClassDefinition(); classes.add(defaultClass); } else { for (SClassDefinition c : classes) { if (c.getName().getName().equals(cname)) { defaultClass = c; return; } } throw new Exception("Class " + cname + " not loaded"); } } @Override public String getDefaultName() { return defaultClass.getName().getName(); } @Override public File getDefaultFile() { return defaultClass.getName().getLocation().getFile(); } @Override public Set<File> getSourceFiles() { return classes.getSourceFiles(); } public ClassListInterpreter getClasses() { return classes; } @Override public String getInitialContext() { return initialContext.toString() + (createdValues.isEmpty() ? "" : Utils.listToString("", createdValues.asList(), "\n", "\n")); } @Override public Environment getGlobalEnvironment() { return new PublicClassEnvironment(assistantFactory, classes, null); } @Override public void init(DBGPReader dbgp) { BasicSchedulableThread.terminateAll(); VdmRuntime.initialize(); RuntimeValidator.init(this); InitThread iniThread = new InitThread(Thread.currentThread()); BasicSchedulableThread.setInitialThread(iniThread); scheduler.init(); SystemClock.init(); CPUValue.init(scheduler, assistantFactory); BUSValue.init(); ObjectValue.init(); logSwapIn(); initialContext = classes.initialize(assistantFactory, dbgp); classes.systemInit(scheduler, dbgp, initialContext); logSwapOut(); createdValues = new NameValuePairMap(); createdDefinitions = assistantFactory.createPDefinitionSet(); scheduler.reset(); // Required before a run, as well as init above BUSValue.start(); // Start any BUS threads first... } @Override public void traceInit(DBGPReader dbgp) { BasicSchedulableThread.terminateAll(); scheduler.reset(); SystemClock.init(); initialContext = classes.initialize(assistantFactory, dbgp); createdValues = new NameValuePairMap(); createdDefinitions = assistantFactory.createPDefinitionSet(); } @Override protected PExp parseExpression(String line, String module) throws Exception { LexTokenReader ltr = new LexTokenReader(line, Settings.dialect, Console.charset); ExpressionReader reader = new ExpressionReader(ltr); reader.setCurrentModule(module); PExp ast = reader.readExpression(); LexToken end = ltr.getLast(); if (!end.is(VDMToken.EOF)) { throw new ParserException(2330, "Tokens found after expression at " + end, new LexLocation(), 0); } return ast; } private Value execute(PExp expr, DBGPReader dbgp) throws Exception { Context mainContext = new StateContext(assistantFactory, defaultClass.getName().getLocation(), "global static scope"); mainContext.putAll(initialContext); mainContext.putAll(createdValues); mainContext.setThreadState(dbgp, CPUValue.vCPU); clearBreakpointHits(); // scheduler.reset(); InitThread iniThread = new InitThread(Thread.currentThread()); BasicSchedulableThread.setInitialThread(iniThread); MainThread main = new MainThread(expr, mainContext); main.start(); scheduler.start(main); if (Settings.dialect == Dialect.VDM_RT && RTLogger.getLogSize() > 0) { RTLogger.dump(true); } RuntimeValidator.stop(); return main.getResult(); // Can throw ContextException } /** * Parse the line passed, type check it and evaluate it as an expression in the initial context. * * @param line * A VDM expression. * @return The value of the expression. * @throws Exception * Parser, type checking or runtime errors. */ @Override public Value execute(String line, DBGPReader dbgp) throws Exception { PExp expr = parseExpression(line, getDefaultName()); Environment env = getGlobalEnvironment(); Environment created = new FlatCheckedEnvironment(assistantFactory, createdDefinitions.asList(), env, NameScope.NAMESANDSTATE); typeCheck(expr, created); return execute(expr, dbgp); } /** * Parse the line passed, and evaluate it as an expression in the context passed. * * @param line * A VDM expression. * @param ctxt * The context in which to evaluate the expression. * @return The value of the expression. * @throws Exception * Parser or runtime errors. */ @Override public Value evaluate(String line, Context ctxt) throws Exception { PExp expr = parseExpression(line, getDefaultName()); PublicClassEnvironment globals = new PublicClassEnvironment(assistantFactory, classes, null); Environment env = new PrivateClassEnvironment(assistantFactory, defaultClass, globals); try { typeCheck(expr, env); } catch (VDMErrorsException e) { // We don't care... we just needed to type check it. } ctxt.threadState.init(); try { return expr.apply(VdmRuntime.getExpressionEvaluator(), ctxt); } catch (Exception e) { throw e; } } @Override public SClassDefinition findClass(String classname) { LexNameToken name = new LexNameToken("CLASS", classname, null); return (SClassDefinition) assistantFactory.createSClassDefinitionAssistant().findType(classes, name); } @Override protected ANamedTraceDefinition findTraceDefinition(LexNameToken name) { PDefinition d = assistantFactory.createSClassDefinitionAssistant().findName(classes, name, NameScope.NAMESANDSTATE); if (d == null || !(d instanceof ANamedTraceDefinition)) { return null; } return (ANamedTraceDefinition) d; } @Override public Value findGlobal(LexNameToken name) { // The name will not be type-qualified, so we can't use the usual // findName methods for (SClassDefinition c : classes) { for (PDefinition d : c.getDefinitions()) { if (assistantFactory.createPDefinitionAssistant().isFunctionOrOperation(d)) { NameValuePairList nvpl = assistantFactory.createPDefinitionAssistant().getNamedValues(d, initialContext); for (NameValuePair n : nvpl) { if (n.name.matches(name)) { return n.value; } } } } for (PDefinition d : c.getAllInheritedDefinitions()) { if (assistantFactory.createPDefinitionAssistant().isFunctionOrOperation(d)) { NameValuePairList nvpl = assistantFactory.createPDefinitionAssistant().getNamedValues(d, initialContext); for (NameValuePair n : nvpl) { if (n.name.matches(name)) { return n.value; } } } } } return null; } @Override public PStm findStatement(File file, int lineno) { return assistantFactory.createSClassDefinitionAssistant().findStatement(classes, file, lineno); } @Override public PExp findExpression(File file, int lineno) { return assistantFactory.createSClassDefinitionAssistant().findExpression(classes, file, lineno); } public void create(String var, String exp) throws Exception { PExp expr = parseExpression(exp, getDefaultName()); Environment env = getGlobalEnvironment(); Environment created = new FlatCheckedEnvironment(assistantFactory, createdDefinitions.asList(), env, NameScope.NAMESANDSTATE); PType type = typeCheck(expr, created); Value v = execute(exp, null); ILexLocation location = defaultClass.getLocation(); LexNameToken n = new LexNameToken(defaultClass.getName().getName(), var, location); createdValues.put(n, v); createdDefinitions.add(AstFactory.newALocalDefinition(location, n, NameScope.LOCAL, type)); } @Override public ProofObligationList getProofObligations() throws AnalysisException { return classes.getProofObligations(assistantFactory); } private void logSwapIn() { // Show the "system constructor" thread creation ISchedulableThread thread = BasicSchedulableThread.getThread(Thread.currentThread()); RTLogger.log(new RTThreadCreateMessage(thread, CPUValue.vCPU.resource)); RTLogger.log(new RTThreadSwapMessage(SwapType.In, thread, CPUValue.vCPU.resource, 0, 0)); } private void logSwapOut() { ISchedulableThread thread = BasicSchedulableThread.getThread(Thread.currentThread()); RTLogger.log(new RTThreadSwapMessage(SwapType.Out, thread, CPUValue.vCPU.resource, 0, 0)); RTLogger.log(new RTThreadKillMessage(thread, CPUValue.vCPU.resource)); } @Override public Context getInitialTraceContext(ANamedTraceDefinition tracedef, boolean debug) throws AnalysisException { ObjectValue object = null; SClassDefinition classdef = tracedef.getClassDefinition(); // Create a new test object object = assistantFactory.createSClassDefinitionAssistant().newInstance(classdef, null, null, initialContext); Context ctxt = new ObjectContext(assistantFactory, classdef.getName().getLocation(), classdef.getName().getName() + "()", initialContext, object); ctxt.put(classdef.getName().getSelfName(), object); return ctxt; } @Override public List<Object> runOneTrace(ANamedTraceDefinition tracedef, CallSequence test, boolean debug) throws AnalysisException { List<Object> list = new Vector<Object>(); Context ctxt = null; try { ctxt = getInitialTraceContext(tracedef, debug); } catch (ValueException e) { list.add(e.getMessage()); return list; } clearBreakpointHits(); // scheduler.reset(); CTMainThread main = new CTMainThread(test, ctxt, debug); main.start(); scheduler.start(main); // Ensures all threads are terminated for next trace run BasicSchedulableThread.signalAll(Signal.TERMINATE); while (main.getRunState() != RunState.COMPLETE) { try { Thread.sleep(10); } catch (InterruptedException e) { break; } } return main.getList(); } @Override public PType findType(String typename) { for (SClassDefinition cDef : classes) { for (PDefinition def : cDef.getDefinitions()) { if (def instanceof ATypeDefinition) { if (def.getName().equals(typename)) { return def.getType(); } } } } return null; } }