Found a hilarious bug where numbers greater than 255 were truncated due to bitwise and with FF on all the shift ops

This commit is contained in:
Mike Nolan 2024-09-01 22:43:10 -05:00
parent 5b32dd7036
commit 93c6f9eda1
2 changed files with 364 additions and 48 deletions

View File

@ -1,11 +1,11 @@
<!--tws
d = list();
d.add(5);
d.add(6);
d.add("Demi Lovato");
for(i = 0;i<d.count;i++)
l = list();
l.add(52);
l.add(52);
l.add(69);
each(l)
{
print d.at(i);
print item;
print "\n";
}
-->

View File

@ -120,13 +120,25 @@ namespace Tesses::WebServer::ScriptEngine
};
typedef std::variant<std::string,ObjectType> IttrType;
typedef std::variant<Undefined,Null,ObjectType,std::string,int64_t,double,bool,char> ScriptType;
class IttrKVP {
public:
IttrType ittr;
int64_t index;
};
typedef std::variant<Undefined,Null,ObjectType,IttrKVP,std::string,int64_t,double,bool,char> ScriptType;
class Ittr : public Object {
public:
IttrType ittr;
int64_t index;
};
inline bool Equals(ScriptType& left, ScriptType& right)
{
@ -191,7 +203,6 @@ namespace Tesses::WebServer::ScriptEngine
}
if(std::holds_alternative<bool>(st))
{
std::cout << (std::get<bool>(st) ? "true" : "false") << std::endl;
return std::get<bool>(st);
}
if(std::holds_alternative<char>(st))
@ -209,7 +220,12 @@ namespace Tesses::WebServer::ScriptEngine
inline std::string ConvertToString(ScriptType& st);
inline std::string ConvertToString(ScriptType& st)
{
if(std::holds_alternative<IttrKVP>(st))
{
auto v = IttrGetValue(st);
return IttrGetKey(st) + ": " + ConvertToString(v);
}
if(std::holds_alternative<Null>(st))
{
@ -460,6 +476,173 @@ namespace Tesses::WebServer::ScriptEngine
}
};
inline ScriptType CreateIttrFor(ScriptType& st)
{
if(std::holds_alternative<std::string>(st))
{
Ittr* ittr =new Ittr();
ittr->index = -1;
ittr->ittr = std::get<std::string>(st);
ObjectType t(ittr);
return t;
}
if(std::holds_alternative<ObjectType>(st))
{
auto ot = std::get<ObjectType>(st).data;
auto ls = dynamic_cast<List*>(ot);
auto dict = dynamic_cast<Dictionary*>(ot);
if(ls != nullptr)
{
Ittr* ittr=new Ittr();
ittr->index = -1;
ittr->ittr = ObjectType(ls);
ObjectType t(ittr);
return t;
}
if(dict != nullptr)
{
Ittr* ittr=new Ittr();
ittr->index = -1;
ittr->ittr = ObjectType(dict);
ObjectType t(ittr);
return t;
}
}
return Null();
}
inline bool IttrMoveNext(ScriptType& st)
{
if(std::holds_alternative<ObjectType>(st))
{
auto objIttr = std::get<ObjectType>(st).data;
auto ittr = dynamic_cast<Ittr*>(objIttr);
if(ittr == nullptr) return false;
if(std::holds_alternative<std::string>(ittr->ittr))
{
std::string str = std::get<std::string>(ittr->ittr);
if(ittr->index >= -1 && ittr->index + 1 < str.size())
{
(ittr->index)++;
return true;
}
}
if(std::holds_alternative<ObjectType>(ittr->ittr))
{
auto ot = std::get<ObjectType>(ittr->ittr).data;
auto ls = dynamic_cast<List*>(ot);
auto dict = dynamic_cast<Dictionary*>(ot);
if(ls != nullptr)
{
if(ittr->index >= -1 && ittr->index + 1 < ls->items.size())
{
(ittr->index)++;
return true;
}
}
if(dict != nullptr)
{
if(ittr->index >= -1 && ittr->index + 1 < dict->items.size())
{
(ittr->index)++;
return true;
}
}
}
}
return false;
}
inline ScriptType IttrCurrent(ScriptType& st)
{
if(std::holds_alternative<ObjectType>(st))
{
auto objIttr = std::get<ObjectType>(st).data;
auto ittr = dynamic_cast<Ittr*>(objIttr);
if(ittr == nullptr) return Null();
if(std::holds_alternative<std::string>(ittr->ittr))
{
std::string str = std::get<std::string>(ittr->ittr);
if(ittr->index >= 0 && ittr->index < str.size())
{
return str[ittr->index];
}
}
if(std::holds_alternative<ObjectType>(ittr->ittr))
{
auto ot = std::get<ObjectType>(ittr->ittr).data;
auto ls = dynamic_cast<List*>(ot);
auto dict = dynamic_cast<Dictionary*>(ot);
if(ls != nullptr)
{
if(ittr->index >= 0 && ittr->index < ls->items.size())
{
return ls->items[ittr->index];
}
}
if(dict != nullptr)
{
if(ittr->index >= 0 && ittr->index < dict->items.size())
{
IttrKVP kvp;
kvp.index = ittr->index;
kvp.ittr = ittr->ittr;
return kvp;
}
}
}
}
return Null();
}
inline std::string IttrGetKey(ScriptType& st)
{
if(std::holds_alternative<IttrKVP>(st))
{
auto ittr = std::get<IttrKVP>(st);
if(std::holds_alternative<ObjectType>(ittr.ittr))
{
auto ot = std::get<ObjectType>(ittr.ittr).data;
auto dict = dynamic_cast<Dictionary*>(ot);
if(dict != nullptr)
{
if(ittr.index >= 0 && ittr.index < dict->items.size())
{
return dict->items[ittr.index].first;
}
}
}
}
return std::string({});
}
inline ScriptType IttrGetValue(ScriptType& st)
{
if(std::holds_alternative<IttrKVP>(st))
{
auto ittr = std::get<IttrKVP>(st);
if(std::holds_alternative<ObjectType>(ittr.ittr))
{
auto ot = std::get<ObjectType>(ittr.ittr).data;
auto dict = dynamic_cast<Dictionary*>(ot);
if(dict != nullptr)
{
if(ittr.index >= 0 && ittr.index < dict->items.size())
{
return dict->items[ittr.index].second;
}
}
}
}
return Null();
}
inline ScriptType TypeOf(Environment* env, std::vector<ScriptType> args);
class RootEnvironment : public Environment
{
@ -615,7 +798,7 @@ namespace Tesses::WebServer::ScriptEngine
auto v = TypeOf(env,args);
this->env->print("IP: ");
this->env->print((int64_t)ip);
this->env->print(" Unexpected type: ");
this->env->print("Unexpected type: ");
this->env->print(v);
this->env->print(" with value ");
this->env->print(p);
@ -896,7 +1079,19 @@ namespace Tesses::WebServer::ScriptEngine
auto obj = std::get<ObjectType>(member).data;
auto ls = dynamic_cast<List*>(obj);
auto dict = dynamic_cast<Dictionary*>(obj);
auto ittr = dynamic_cast<Ittr*>(obj);
if(ittr != nullptr)
{
if(key == "current")
{
return IttrCurrent(member);
}
if(key == "movenext")
{
return IttrMoveNext(member);
}
}
if(ls != nullptr)
{
if(key == "add" && args.size() > 0)
@ -1135,7 +1330,18 @@ namespace Tesses::WebServer::ScriptEngine
return (int64_t)str.size();
}
}
if(std::holds_alternative<IttrKVP>(instance))
{
if(name == "key")
{
return IttrGetKey(instance);
}
if(name == "value")
{
return IttrGetValue(instance);
}
}
if(std::holds_alternative<ObjectType>(instance))
{
auto inst = std::get<ObjectType>(instance).data;
@ -1352,14 +1558,14 @@ namespace Tesses::WebServer::ScriptEngine
if(ip + 8 <= bytecodeLength)
{
uint64_t value = ((uint64_t)bytecode[ip++] << 56) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 48) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 40) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 32) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 24) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 16) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 8) & 0xFF;
value |= (uint64_t)bytecode[ip++] & 0xFF;
uint64_t value = ((uint64_t)bytecode[ip++] << 56);
value |= ((uint64_t)bytecode[ip++] << 48);
value |= ((uint64_t)bytecode[ip++] << 40);
value |= ((uint64_t)bytecode[ip++] << 32);
value |= ((uint64_t)bytecode[ip++] << 24);
value |= ((uint64_t)bytecode[ip++] << 16);
value |= ((uint64_t)bytecode[ip++] << 8);
value |= (uint64_t)bytecode[ip++];
stack.push((int64_t)value);
}
@ -1369,14 +1575,14 @@ namespace Tesses::WebServer::ScriptEngine
{
if(ip + 8 <= bytecodeLength)
{
uint64_t value = ((uint64_t)bytecode[ip++] << 56) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 48) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 40) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 32) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 24) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 16) & 0xFF;
value |= ((uint64_t)bytecode[ip++] << 8) & 0xFF;
value |= (uint64_t)bytecode[ip++] & 0xFF;
uint64_t value = ((uint64_t)bytecode[ip++] << 56);
value |= ((uint64_t)bytecode[ip++] << 48);
value |= ((uint64_t)bytecode[ip++] << 40);
value |= ((uint64_t)bytecode[ip++] << 32);
value |= ((uint64_t)bytecode[ip++] << 24);
value |= ((uint64_t)bytecode[ip++] << 16);
value |= ((uint64_t)bytecode[ip++] << 8);
value |= (uint64_t)bytecode[ip++];
stack.push(*(double*)&value);
@ -1390,10 +1596,10 @@ namespace Tesses::WebServer::ScriptEngine
{
size_t len = (bytecode[ip++] << 24) & 0xFF;
len |= (bytecode[ip++] << 16) & 0xFF;
len |= (bytecode[ip++] << 8) & 0xFF;
len |= bytecode[ip++] & 0xFF;
size_t len = ((size_t)bytecode[ip++] << 24);
len |= ((size_t)bytecode[ip++] << 16);
len |= ((size_t)bytecode[ip++] << 8);
len |= (size_t)bytecode[ip++];;
if(ip + len <= bytecodeLength)
{
@ -1549,10 +1755,10 @@ namespace Tesses::WebServer::ScriptEngine
{
size_t len = (bytecode[ip++] << 24) & 0xFF;
len |= (bytecode[ip++] << 16) & 0xFF;
len |= (bytecode[ip++] << 8) & 0xFF;
len |= bytecode[ip++] & 0xFF;
size_t len = ((size_t)bytecode[ip++] << 24);
len |= ((size_t)bytecode[ip++] << 16);
len |= ((size_t)bytecode[ip++] << 8);
len |= (size_t)bytecode[ip++];
if(len < bytecodeLength)
ip = len;
else
@ -1569,10 +1775,10 @@ namespace Tesses::WebServer::ScriptEngine
{
size_t len = (bytecode[ip++] << 24) & 0xFF;
len |= (bytecode[ip++] << 16) & 0xFF;
len |= (bytecode[ip++] << 8) & 0xFF;
len |= bytecode[ip++] & 0xFF;
size_t len = ((size_t)bytecode[ip++] << 24);
len |= ((size_t)bytecode[ip++] << 16);
len |= ((size_t)bytecode[ip++] << 8);
len |= (size_t)bytecode[ip++];
if(len < bytecodeLength)
{
auto v = Pop();
@ -1588,7 +1794,12 @@ namespace Tesses::WebServer::ScriptEngine
}
}
break;
case Instruction::ITTR:
{
auto ittr=Pop();
stack.push(CreateIttrFor(ittr));
}
break;
case Instruction::GETFIELD:
{
std::string name = PopString();
@ -1886,7 +2097,24 @@ namespace Tesses::WebServer::ScriptEngine
}
};
class EachNode : public Node {
public:
Node* item;
Node* list;
Node* body;
EachNode(Node* item,Node* list,Node* body)
{
this->item = item;
this->list = list;
this->body = body;
}
~EachNode()
{
delete item;
delete list;
delete body;
}
};
class FunctionDeclarationNode : public Node {
public:
@ -2937,7 +3165,26 @@ namespace Tesses::WebServer::ScriptEngine
NextTokenIs(";");
return new WhileNode(cond,body,true);
}
if(NextTokenIs("each"))
{
Ensure("(");
Node* list = ParseExpression();
Node* item = nullptr;
if(NextTokenIs(":"))
{
item = list;
list = ParseExpression();
}
else
{
item = new GetVariableExpression("item");
}
Ensure(")");
Node* body = ParseNode();
NextTokenIs(";");
return new EachNode(item,list,body);
}
if(NextTokenIs("func"))
{
Node* fn = ParseExpression();
@ -2984,6 +3231,10 @@ namespace Tesses::WebServer::ScriptEngine
return "double";
}
if(std::holds_alternative<IttrKVP>(item))
{
return "ittrkvp";
}
if(std::holds_alternative<bool>(item))
{
return "bool";
@ -3003,7 +3254,8 @@ namespace Tesses::WebServer::ScriptEngine
auto dict = dynamic_cast<Dictionary*>(ot);
auto ifunc = dynamic_cast<InternalFunction*>(ot);
auto efunc = dynamic_cast<ExternalFunction*>(ot);
auto ittr = dynamic_cast<Ittr*>(ot);
if(ittr != nullptr) return "ittr";
if(ls != nullptr) return "list";
if(dict != nullptr) return "dictionary";
if(ifunc != nullptr) return "internal_function";
@ -3276,6 +3528,7 @@ namespace Tesses::WebServer::ScriptEngine
std::string _cont = {};
std::stack<std::string> __eachs;
int jmpId = 0;
std::string NewJumpId()
@ -3341,6 +3594,7 @@ namespace Tesses::WebServer::ScriptEngine
auto whileNode = dynamic_cast<WhileNode*>(n);
auto breakNode = dynamic_cast<BreakNode*>(n);
auto cont = dynamic_cast<ContinueNode*>(n);
auto eachLoop = dynamic_cast<EachNode*>(n);
auto forLoop = dynamic_cast<ForNode*>(n);
auto lOr = dynamic_cast<LogicalOrExpression*>(n);
auto lAnd = dynamic_cast<LogicalAndExpression*>(n);
@ -3382,6 +3636,13 @@ namespace Tesses::WebServer::ScriptEngine
}
if(retNode != nullptr)
{
while(!this->__eachs.empty())
{
auto item = this->__eachs.top();
GetVariableExpression gve(item);
GenerateFree(&gve,instructions);
this->__eachs.pop();
}
Compile(retNode->retVal,instructions);
instructions.push_back(new SimpleInstruction(Instruction::RET));
}
@ -3473,7 +3734,62 @@ namespace Tesses::WebServer::ScriptEngine
_cont = oldcont;
_brk = oldbrk;
}
if(eachLoop != nullptr)
{
instructions.push_back(new SimpleInstruction(Instruction::SET_LOOP));
std::string eachIttr = NewJumpId();
__eachs.push(eachIttr);
instructions.push_back(new StringInstruction(eachIttr));
Compile(eachLoop->list,instructions);
instructions.push_back(new SimpleInstruction(Instruction::ITTR));
instructions.push_back(new SimpleInstruction(Instruction::SETVARIABLE));
instructions.push_back(new SimpleInstruction(Instruction::POP));
std::string oldcont = _cont;
std::string oldbrk = _brk;
std::string yes = NewJumpId();
std::string end = NewJumpId();
std::string begin = NewJumpId();
_cont = begin;
_brk = end;
instructions.push_back(new LabelInstruction(begin));
GetVariableExpression var(eachIttr);
GetMemberExpression mem(&var,"movenext");
FunctionCallExpression ce(&mem);
Compile(&ce,instructions);
ce.name = nullptr;
mem.parent=nullptr;
instructions.push_back(new JumpInstruction(Instruction::JMPC,yes));
instructions.push_back(new JumpInstruction(Instruction::JMP,end));
instructions.push_back(new LabelInstruction(yes));
GetMemberExpression mem2(&var,"current");
FunctionCallExpression ce2(&mem2);
AssignExpression assignExpr(eachLoop->item,&ce2);
Compile(&assignExpr,instructions);
ExpressionPop(&assignExpr,instructions);
mem2.parent=nullptr;
ce2.name=nullptr;
assignExpr.left=nullptr;
assignExpr.right = nullptr;
Compile(eachLoop->body,instructions);
ExpressionPop(eachLoop->body,instructions);
instructions.push_back(new JumpInstruction(Instruction::JMP,begin));
instructions.push_back(new LabelInstruction(end));
__eachs.pop();
GenerateFree(&var,instructions);
instructions.push_back(new SimpleInstruction(Instruction::DESTROY_LOOP));
_cont = oldcont;
_brk = oldbrk;
}
if(whileNode != nullptr)
{
if(whileNode->isDo)