diff --git a/JSImpl/src/ASTInterpreter.cpp b/JSImpl/src/ASTInterpreter.cpp index 25f5c021..c0c40941 100644 --- a/JSImpl/src/ASTInterpreter.cpp +++ b/JSImpl/src/ASTInterpreter.cpp @@ -1,11 +1,14 @@ #include "ASTInterpreter.h" #include "Expression.h" #include +#include #include #undef NOT_IMPLEMENTED #define NOT_IMPLEMENTED (void)e; assert(0 && "not-implemented") +#define FLOAT_EQUALS(first, second) fabs(first - second) < std::numeric_limits::epsilon() + struct LValueExtractor : public ExpressionVisitor { public: @@ -70,6 +73,9 @@ struct StackScope }; ASTInterpreter::ASTInterpreter() + : m_Fallthrough(false) + , m_Break(false) + , m_Continue(false) { // Allow globals EnterScope(); @@ -101,6 +107,15 @@ bool ASTInterpreter::EvalToBool(const ExpressionPtr& e) return result != 0.0; } +bool ASTInterpreter::EvalIsEqual(const ExpressionPtr& e) +{ + e->Accept(*this); + auto second = m_Evaluation.back(); + m_Evaluation.pop_back(); + auto first = m_Evaluation.back(); + return FLOAT_EQUALS(first, second); +} + void ASTInterpreter::EnterScope() { m_Scopes.push(Scope{}); @@ -137,15 +152,20 @@ bool ASTInterpreter::HasVariable(const IPLString& name) return m_Variables.find(name) != m_Variables.end(); } +bool ASTInterpreter::IsInSkipMode() +{ + return m_Break || m_Continue; +} + void ASTInterpreter::Visit(LiteralNull* e) { - NOT_IMPLEMENTED; + NOT_IMPLEMENTED; } void ASTInterpreter::Visit(LiteralUndefined* e) { - NOT_IMPLEMENTED; + NOT_IMPLEMENTED; } void ASTInterpreter::Visit(LiteralString* e) { - NOT_IMPLEMENTED; + NOT_IMPLEMENTED; } void ASTInterpreter::Visit(LiteralObject* e) @@ -198,10 +218,10 @@ void ASTInterpreter::Visit(BinaryExpression* e) { m_Evaluation.push_back(right); break; case TokenType::EqualEqual: - m_Evaluation.push_back(fabs(left - right) < 0.0001); + m_Evaluation.push_back(FLOAT_EQUALS(left, right)); break; case TokenType::BangEqual: - m_Evaluation.push_back(fabs(left - right) > 0.0001); + m_Evaluation.push_back(FLOAT_EQUALS(left, right)); break; case TokenType::Equal: { @@ -216,6 +236,18 @@ void ASTInterpreter::Visit(BinaryExpression* e) { } void ASTInterpreter::Visit(UnaryExpression* e) { + switch (e->GetOperator()) + { + case TokenType::Break: + m_Break = true; + return; + case TokenType::Continue: + m_Continue = true; + return; + default: + ; + } + LValueExtractor extractor(this); auto lvalue = extractor.Run(e->GetExpr().get()); if (e->GetSuffix()) @@ -278,19 +310,45 @@ void ASTInterpreter::Visit(IfStatement* e) { } } -void ASTInterpreter::Visit(SwitchStatement* e) { NOT_IMPLEMENTED;} -void ASTInterpreter::Visit(CaseStatement *e) { NOT_IMPLEMENTED;} +void ASTInterpreter::Visit(SwitchStatement* e) +{ + VariableScope scope(this); + e->GetCondition()->Accept(*this); + for (const auto& stmt : e->GetCases()) + stmt->Accept(*this); + RunExpression(e->GetDefaultCase()); + m_Evaluation.pop_back(); + + m_Fallthrough = false; + m_Break = false; +} + +void ASTInterpreter::Visit(CaseStatement *e) +{ + if (m_Break) + return; + + if (m_Fallthrough || EvalIsEqual(e->GetCondition())) + { + RunExpression(e->GetBody()); + m_Fallthrough = true; + } +} void ASTInterpreter::Visit(WhileStatement* e) { VariableScope scope(this); if (e->GetDoWhile()) { VariableScope bodyScope(this); RunExpression(e->GetBody()); + m_Continue = false; } while (EvalToBool(e->GetCondition())) { VariableScope bodyScope(this); RunExpression(e->GetBody()); + m_Continue = false; } + + m_Break = false; } void ASTInterpreter::Visit(ForStatement* e) { @@ -305,12 +363,15 @@ void ASTInterpreter::Visit(ForStatement* e) { { VariableScope bodyScope(this); RunExpression(e->GetBody()); + m_Continue = false; } { StackScope stackScope(this); RunExpression(e->GetIteration()); } } + + m_Break = false; } void ASTInterpreter::Visit(FunctionDeclaration* e) { NOT_IMPLEMENTED;} diff --git a/JSImpl/src/ASTInterpreter.h b/JSImpl/src/ASTInterpreter.h index db890130..c1c5658a 100644 --- a/JSImpl/src/ASTInterpreter.h +++ b/JSImpl/src/ASTInterpreter.h @@ -16,6 +16,8 @@ class ASTInterpreter : public ExpressionVisitor ValueStack Run(Expression* program); + virtual bool IsInSkipMode() override; + virtual void Visit(LiteralNull* e) override; virtual void Visit(LiteralUndefined* e) override; virtual void Visit(LiteralString* e) override; @@ -52,6 +54,7 @@ class ASTInterpreter : public ExpressionVisitor private: void RunExpression(const ExpressionPtr& e); bool EvalToBool(const ExpressionPtr& e); + bool EvalIsEqual(const ExpressionPtr& e); void EnterScope(); void LeaveScope(); @@ -63,4 +66,8 @@ class ASTInterpreter : public ExpressionVisitor typedef IPLVector Scope; IPLStack m_Scopes; + + bool m_Fallthrough; + bool m_Break; + bool m_Continue; }; diff --git a/JSImpl/src/ByteCodeGenerator.cpp b/JSImpl/src/ByteCodeGenerator.cpp index fd853264..bcca27fe 100644 --- a/JSImpl/src/ByteCodeGenerator.cpp +++ b/JSImpl/src/ByteCodeGenerator.cpp @@ -22,6 +22,8 @@ class ByteCodeGenerator : public ExpressionVisitor virtual void Visit(EmptyExpression* e) override { (void)e; } virtual void Visit(IfStatement* e) override; virtual void Visit(ForStatement* e) override; + virtual void Visit(SwitchStatement* e) override; + virtual void Visit(CaseStatement* e) override; virtual void Visit(UnaryExpression* e) override; IPLString GetCode(); @@ -106,6 +108,10 @@ class ByteCodeGenerator : public ExpressionVisitor IPLStack m_RegisterStack; IPLString m_OutputCode; ByteCodeGeneratorOptions m_Options; + + IPLVector> m_SwitchCases; + IPLVector m_BreakInstructionAddresses; + IPLVector m_ContinueInstructionAddresses; }; size_t ByteCodeGenerator::PushInstruction(Instruction::Type opcode, const IPLString& arg0, const IPLString& arg1, const IPLString& arg2) @@ -343,6 +349,55 @@ void ByteCodeGenerator::Visit(ForStatement* e) e->GetIteration()->Accept(*this); PushInstruction(Instruction::Type::JMP, compareAddress); m_Code[endAddress].Values.Address[0] = m_Code.size(); + + for (auto breakInstructionAddress : m_BreakInstructionAddresses) + m_Code[breakInstructionAddress].Values.Address[0] = m_Code.size(); + m_BreakInstructionAddresses.clear(); + + for (auto continueInstructionAddress : m_ContinueInstructionAddresses) + m_Code[continueInstructionAddress].Values.Address[0] = compareAddress; + m_ContinueInstructionAddresses.clear(); +} + +void ByteCodeGenerator::Visit(SwitchStatement* e) +{ + // unfortunately, we cannot use a jump table for the switch as JS allows non-integer values as conditions + e->GetCondition()->Accept(*this); + for (const auto& c : e->GetCases()) + { + c->Accept(*this); + } + + auto jumpToDefaultAddress = PushInstruction(Instruction::Type::JMP); + + for (const auto& c : m_SwitchCases) + { + auto jumpAddress = c.first; + m_Code[jumpAddress].Values.Address[0] = m_Code.size(); + c.second->Accept(*this); + } + + m_Code[jumpToDefaultAddress].Values.Address[0] = m_Code.size(); + e->GetDefaultCase()->Accept(*this); + + m_SwitchCases.clear(); + m_RegisterStack.pop(); + + for (auto breakInstructionAddress : m_BreakInstructionAddresses) + m_Code[breakInstructionAddress].Values.Address[0] = m_Code.size(); + m_BreakInstructionAddresses.clear(); +} + +void ByteCodeGenerator::Visit(CaseStatement* e) +{ + e->GetCondition()->Accept(*this); + auto caseCondition = m_RegisterStack.top(); + m_RegisterStack.pop(); + auto switchCondition = m_RegisterStack.top(); + auto result = CreateRegister(); + PushInstruction(Instruction::Type::EQ, result, switchCondition, caseCondition); + auto jumpAddress = PushInstruction(Instruction::Type::JMPT, result, (size_t)0); + m_SwitchCases.emplace_back(jumpAddress, e->GetBody()); } void ByteCodeGenerator::Visit(IdentifierExpression* e) @@ -391,6 +446,18 @@ void ByteCodeGenerator::Visit(UnaryExpression* e) PushInstruction(Instruction::Type::SUB, reg, reg, one); } return; + case TokenType::Break: + { + auto breakAddress = PushInstruction(Instruction::Type::JMP); + m_BreakInstructionAddresses.push_back(breakAddress); + } + return; + case TokenType::Continue: + { + auto continueAddress = PushInstruction(Instruction::Type::JMP); + m_ContinueInstructionAddresses.push_back(continueAddress); + } + return; default: NOT_IMPLEMENTED; break; @@ -654,7 +721,7 @@ IPLString ByteCodeGenerator::GetCode() default: NOT_IMPLEMENTED; break; - + } ++programCounter; } diff --git a/JSImpl/src/Expression.h b/JSImpl/src/Expression.h index 3fa7b90c..745f0d9a 100644 --- a/JSImpl/src/Expression.h +++ b/JSImpl/src/Expression.h @@ -40,7 +40,7 @@ class Expression : public IPLEnableShared : \ MEMBERS_ITERATOR(EXPAND_INITIALIZER_LIST) m_dummy(__dummy) {} \ virtual ~ClassName() {} \ - virtual void Accept(ExpressionVisitor& v) override { v.Visit(this); } \ + virtual void Accept(ExpressionVisitor& v) override { if (!v.IsInSkipMode()) v.Visit(this); } \ \ MEMBERS_ITERATOR(GENERATE_GETTERS) \ private: \ diff --git a/JSImpl/src/ExpressionVisitor.h b/JSImpl/src/ExpressionVisitor.h index f1f2fcd1..e82d534a 100644 --- a/JSImpl/src/ExpressionVisitor.h +++ b/JSImpl/src/ExpressionVisitor.h @@ -7,6 +7,8 @@ class ExpressionVisitor public: ~ExpressionVisitor() {} + virtual bool IsInSkipMode() { return false; } + virtual void Visit(LiteralNull* e) { (void)e; NOT_IMPLEMENTED; } virtual void Visit(LiteralUndefined* e) { (void)e; NOT_IMPLEMENTED; } virtual void Visit(LiteralString* e) { (void)e; NOT_IMPLEMENTED; }