using System.Collections.Generic; using System.Linq; using Jint.Native; using Jint.Native.Errors; using Jint.Native.Function; using Jint.Parser.Ast; using Jint.Runtime.Environments; using Jint.Runtime.References; namespace Jint.Runtime { public class StatementInterpreter { private readonly Engine _engine; public StatementInterpreter(Engine engine) { _engine = engine; } private Completion ExecuteStatement(Statement statement) { return _engine.ExecuteStatement(statement); } public Completion ExecuteEmptyStatement(EmptyStatement emptyStatement) { return new Completion(Completion.Normal, null, null); } public Completion ExecuteExpressionStatement(ExpressionStatement expressionStatement) { var exprRef = _engine.EvaluateExpression(expressionStatement.Expression); return new Completion(Completion.Normal, _engine.GetValue(exprRef), null); } public Completion ExecuteIfStatement(IfStatement ifStatement) { var exprRef = _engine.EvaluateExpression(ifStatement.Test); Completion result; if (TypeConverter.ToBoolean(_engine.GetValue(exprRef))) { result = ExecuteStatement(ifStatement.Consequent); } else if (ifStatement.Alternate != null) { result = ExecuteStatement(ifStatement.Alternate); } else { return new Completion(Completion.Normal, null, null); } return result; } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.1 /// /// /// public Completion ExecuteDoWhileStatement(DoWhileStatement doWhileStatement) { object v = null; bool iterating; do { var stmt = ExecuteStatement(doWhileStatement.Body); if (stmt.Value != null) { v = stmt.Value; } if (stmt.Type != Completion.Continue /* todo: || stmt.Target*/) { if (stmt.Type == Completion.Break /* todo: complete */) { return new Completion(Completion.Normal, v, null); } if (stmt.Type != Completion.Normal) { return stmt; } } var exprRef = _engine.EvaluateExpression(doWhileStatement.Test); iterating = TypeConverter.ToBoolean(_engine.GetValue(exprRef)); } while (iterating); return new Completion(Completion.Normal, v, null); } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.2 /// /// /// public Completion ExecuteWhileStatement(WhileStatement whileStatement) { object v = null; while (true) { var exprRef = _engine.EvaluateExpression(whileStatement.Test); if (!TypeConverter.ToBoolean(_engine.GetValue(exprRef))) { return new Completion(Completion.Normal, v, null); } var stmt = ExecuteStatement(whileStatement.Body); if (stmt.Value != null) { v = stmt.Value; } if (stmt.Type != Completion.Continue /* todo: complete */) { if (stmt.Type == Completion.Break /* todo: complete */) { return new Completion(Completion.Normal, v, null); } if (stmt.Type != Completion.Normal) { return stmt; } } } } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.3 /// /// /// public Completion ExecuteForStatement(ForStatement forStatement) { if (forStatement.Init != null) { if (forStatement.Init.Type == SyntaxNodes.VariableDeclaration) { ExecuteStatement(forStatement.Init.As()); } else { _engine.GetValue(_engine.EvaluateExpression(forStatement.Init.As())); } } object v = null; while (true) { if (forStatement.Test != null) { var testExprRef = _engine.EvaluateExpression(forStatement.Test); if (!TypeConverter.ToBoolean(_engine.GetValue(testExprRef))) { return new Completion(Completion.Normal, v, null); } } var stmt = ExecuteStatement(forStatement.Body); if (stmt.Value != null) { v = stmt.Value; } if (stmt.Type == Completion.Break /* todo: complete */) { return new Completion(Completion.Normal, v, null); } if (stmt.Type != Completion.Continue /* todo: complete */) { if (stmt.Type != Completion.Normal) { return stmt; } } if (forStatement.Update != null) { var incExprRef = _engine.EvaluateExpression(forStatement.Update); _engine.GetValue(incExprRef); } } } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4 /// /// /// public Completion ForInStatement(ForInStatement forInStatement) { object varName = null; if (forInStatement.Left.Type == SyntaxNodes.VariableDeclaration) { varName = ExecuteStatement(forInStatement.Left.As()).Value; } var exprRef = _engine.EvaluateExpression(forInStatement.Right); var experValue = _engine.GetValue(exprRef); if (experValue == Undefined.Instance || experValue == Null.Instance) { return new Completion(Completion.Normal, null, null); } var obj = TypeConverter.ToObject(_engine, experValue); object v = null; foreach (var entry in obj.Properties) { if (!entry.Value.Enumerable) { continue; } var p = entry.Key; if (varName != null) { var varRef = varName as Reference; _engine.PutValue(varRef, p); } else { var lhsRef = _engine.EvaluateExpression(forInStatement.Left.As()) as Reference; _engine.PutValue(lhsRef, p); } var stmt = ExecuteStatement(forInStatement.Body); if (stmt.Value != null) { v = stmt.Value; } if (stmt.Type == Completion.Break /* todo: complete */) { return new Completion(Completion.Normal, v, null); } if (stmt.Type != Completion.Continue /* todo: complete */) { if (stmt.Type != Completion.Normal) { return stmt; } } } return new Completion(Completion.Normal, v, null); } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.7 /// /// /// public Completion ExecuteContinueStatement(ContinueStatement continueStatement) { return new Completion(Completion.Continue, null, continueStatement.Label != null ? continueStatement.Label.Name : null); } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.8 /// /// /// public Completion ExecuteBreakStatement(BreakStatement breakStatement) { return new Completion(Completion.Break, null, breakStatement.Label != null ? breakStatement.Label.Name : null); } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.9 /// /// /// public Completion ExecuteReturnStatement(ReturnStatement statement) { if (statement.Argument == null) { return new Completion(Completion.Return, Undefined.Instance, null); } var exprRef = _engine.EvaluateExpression(statement.Argument); return new Completion(Completion.Return, _engine.GetValue(exprRef), null); } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.10 /// /// /// public Completion ExecuteWithStatement(WithStatement withStatement) { var val = _engine.EvaluateExpression(withStatement.Object); var obj = TypeConverter.ToObject(_engine, _engine.GetValue(val)); var oldEnv = _engine.ExecutionContext.LexicalEnvironment; var newEnv = LexicalEnvironment.NewObjectEnvironment(obj, oldEnv); // todo: handle ProvideThis // newEnv.ProvideThis = true; _engine.ExecutionContext.LexicalEnvironment = newEnv; Completion c; try { c = ExecuteStatement(withStatement.Body); } catch (Error v) { c = new Completion(Completion.Throw, v, null); } finally { _engine.ExecutionContext.LexicalEnvironment = oldEnv; } return c; } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.11 /// /// /// public Completion ExecuteSwitchStatement(SwitchStatement switchStatement) { var exprRef = _engine.EvaluateExpression(switchStatement.Discriminant); var r = ExecuteSwitchBlock(switchStatement.Cases, _engine.GetValue(exprRef)); if (r.Type == Completion.Break /* too: complete */) { return new Completion(Completion.Normal, r.Value, null); } return r; } public Completion ExecuteSwitchBlock(IEnumerable switchBlock, object input) { object v = null; SwitchCase defaultCase = null; foreach (var clause in switchBlock) { if (clause.Test == null) { defaultCase = clause; } else { var clauseSelector = _engine.GetValue(_engine.EvaluateExpression(clause.Test)); if (ExpressionInterpreter.StriclyEqual(clauseSelector, input)) { if (clause.Consequent != null) { var r = ExecuteStatementList(clause.Consequent); if (r.Type != Completion.Normal) { return r; } v = r.Value; } } } } if (defaultCase != null) { var r = ExecuteStatementList(defaultCase.Consequent); if (r.Type != Completion.Normal) { return r; } v = r.Value; } return new Completion(Completion.Normal, v, null); } public Completion ExecuteStatementList(IEnumerable statementList) { var c = new Completion(Completion.Normal, null, null); try { foreach (var statement in statementList) { c = ExecuteStatement(statement); if (c.Type != Completion.Normal) { return c; } } } catch(Error v) { return new Completion(Completion.Throw, v, null); } return c; } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.13 /// /// /// public Completion ExecuteThrowStatement(ThrowStatement throwStatement) { var exprRef = _engine.EvaluateExpression(throwStatement.Argument); return new Completion(Completion.Throw, _engine.GetValue(exprRef), null); } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.14 /// /// /// public Completion ExecuteTryStatement(TryStatement tryStatement) { var b = ExecuteStatement(tryStatement.Block); if (b.Type == Completion.Throw) { // execute catch if (tryStatement.Handlers.Any()) { foreach (var catchClause in tryStatement.Handlers) { var c = _engine.GetValue(b); var oldEnv = _engine.ExecutionContext.LexicalEnvironment; var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(oldEnv); catchEnv.Record.CreateMutableBinding(catchClause.Param.Name); catchEnv.Record.SetMutableBinding(catchClause.Param.Name, c, false); _engine.ExecutionContext.LexicalEnvironment = catchEnv; b = ExecuteStatement(catchClause.Body); _engine.ExecutionContext.LexicalEnvironment = oldEnv; } } } if (tryStatement.Finalizer != null) { var f = ExecuteStatement(tryStatement.Finalizer); if (f.Type == Completion.Normal) { return b; } return f; } return b; } public void EvaluateVariableScope(IVariableScope variableScope) { foreach (var variableDeclaration in variableScope.VariableDeclarations) { // declare the variables only ExecuteVariableDeclaration(variableDeclaration, false); } } public Completion ExecuteProgram(Program program) { if (program.Strict) { _engine.Options.Strict(); } EvaluateVariableScope(program); return ExecuteStatementList(program.Body); } public Completion ExecuteVariableDeclaration(VariableDeclaration statement, bool initializeOnly) { var env = _engine.ExecutionContext.VariableEnvironment.Record; object value = Undefined.Instance; foreach (var declaration in statement.Declarations) { var dn = declaration.Id.Name; if (!initializeOnly) { var varAlreadyDeclared = env.HasBinding(dn); if (!varAlreadyDeclared) { env.CreateMutableBinding(dn, true); } } else { if (declaration.Init != null) { value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init)); } env.SetMutableBinding(dn, value, false); } } return new Completion(Completion.Normal, value, null); } public Completion ExecuteBlockStatement(BlockStatement blockStatement) { return ExecuteStatementList(blockStatement.Body); } public Completion ExecuteFunctionDeclaration(FunctionDeclaration functionDeclaration) { // create function objects // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2 var identifier = functionDeclaration.Id.Name; // todo: should be declared in the current context _engine.Global.Set( identifier, new ScriptFunctionInstance( _engine, functionDeclaration.Body, identifier, functionDeclaration.Parameters.ToArray(), _engine.Function.Prototype, _engine.Object.Construct(Arguments.Empty), LexicalEnvironment.NewDeclarativeEnvironment(_engine.ExecutionContext.LexicalEnvironment), functionDeclaration.Strict ) ); return new Completion(Completion.Normal, null, null); } public Completion ExecuteDebuggerStatement(DebuggerStatement debuggerStatement) { throw new System.NotImplementedException(); } } }