Merge pull request #2576 from Kingcom/master

Expression parser update
This commit is contained in:
Henrik Rydgård 2013-07-01 08:23:42 -07:00
commit c28109dfa0
17 changed files with 110 additions and 515 deletions

View File

@ -585,6 +585,8 @@ add_library(native STATIC
native/json/json_writer.h
native/math/curves.cpp
native/math/curves.h
native/math/expression_parser.cpp
native/math/expression_parser.h
native/math/lin/aabb.cpp
native/math/lin/aabb.h
native/math/lin/matrix4x4.cpp
@ -1013,6 +1015,7 @@ if(WIN32)
Windows/Debugger/CtrlRegisterList.cpp
Windows/Debugger/CtrlRegisterList.h
Windows/Debugger/Debugger.h
Windows/Debugger/DebuggerShared.cpp
Windows/Debugger/DebuggerShared.h
Windows/Debugger/Debugger_Disasm.cpp
Windows/Debugger/Debugger_Disasm.h
@ -1025,8 +1028,6 @@ if(WIN32)
Windows/Debugger/Debugger_SymbolMap.h
Windows/Debugger/Debugger_VFPUDlg.cpp
Windows/Debugger/Debugger_VFPUDlg.h
Windows/Debugger/ExpressionParser.cpp
Windows/Debugger/ExpressionParser.h
Windows/Debugger/SimpleELF.h
# Windows/DlgDynaView.cpp
# Windows/DlgDynaView.h

View File

@ -18,6 +18,7 @@
#pragma once
#include <cstdio>
#include "Common/CommonTypes.h"
#include "native/math/expression_parser.h"
struct MemMap;
@ -42,6 +43,8 @@ public:
virtual const char *getDescription(unsigned int address) {return "";}
virtual const char *findSymbolForAddress(unsigned int address) { return NULL; };
virtual bool getSymbolValue(char* symbol, u32& dest) { return false; };
virtual bool initExpression(char* exp, PostfixExpression& dest) { return false; };
virtual bool parseExpression(PostfixExpression& exp, u32& dest) { return false; };
virtual const char *GetName() = 0;
virtual int GetGPRSize() = 0; //32 or 64

View File

@ -26,6 +26,52 @@
#include "../MIPS/MIPS.h"
#include "../System.h"
class MipsExpressionFunctions: public IExpressionFunctions
{
public:
MipsExpressionFunctions(DebugInterface* cpu): cpu(cpu) { };
virtual bool parseReference(char* str, uint32& referenceIndex)
{
for (int i = 0; i < 32; i++)
{
char reg[8];
sprintf(reg,"r%d",i);
if (strcasecmp(str,reg) == 0 || strcasecmp(str,cpu->GetRegName(0,i)) == 0)
{
referenceIndex = i;
return true;
}
}
if (strcasecmp(str,"pc") == 0)
{
referenceIndex = 32;
return true;
}
return false;
}
virtual bool parseSymbol(char* str, uint32& symbolValue)
{
return cpu->getSymbolValue(str,symbolValue);
}
virtual uint32 getReferenceValue(uint32 referenceIndex)
{
if (referenceIndex < 32) return cpu->GetRegValue(0,referenceIndex);
if (referenceIndex == 32) return cpu->GetPC();
return -1;
}
private:
DebugInterface* cpu;
};
const char *MIPSDebugInterface::disasm(unsigned int address, unsigned int align)
{
MIPSState *x = currentCPU;
@ -91,6 +137,17 @@ bool MIPSDebugInterface::getSymbolValue(char* symbol, u32& dest)
return symbolMap.getSymbolValue(symbol,dest);
}
bool MIPSDebugInterface::initExpression(char* exp, PostfixExpression& dest)
{
MipsExpressionFunctions funcs(this);
return initPostfixExpression(exp,&funcs,dest);
}
bool MIPSDebugInterface::parseExpression(PostfixExpression& exp, u32& dest)
{
MipsExpressionFunctions funcs(this);
return parsePostfixExpression(exp,&funcs,dest);
}
void MIPSDebugInterface::runToBreakpoint()
{

View File

@ -42,6 +42,8 @@ public:
virtual const char *getDescription(unsigned int address);
virtual const char *findSymbolForAddress(unsigned int address);
virtual bool getSymbolValue(char* symbol, u32& dest);
virtual bool initExpression(char* exp, PostfixExpression& dest);
virtual bool parseExpression(PostfixExpression& exp, u32& dest);
//overridden functions
const char *GetName();

View File

@ -85,6 +85,7 @@ SOURCES += ../native/audio/*.cpp \
../native/input/gesture_detector.cpp \
../native/json/json_writer.cpp \
../native/math/curves.cpp \
../native/math/expression_parser.cpp \
../native/math/math_util.cpp \
../native/math/lin/*.cpp \
../native/midi/midi_input.cpp \
@ -126,6 +127,7 @@ HEADERS += ../native/audio/*.h \
../native/input/input_state.h \
../native/json/json_writer.h \
../native/math/curves.h \
../native/math/expression_parser.h \
../native/math/lin/*.h \
../native/midi/midi_input.h \
../native/net/*.h \

View File

@ -9,7 +9,6 @@
#include "CtrlDisAsmView.h"
#include "Debugger_MemoryDlg.h"
#include "ExpressionParser.h"
#include "DebuggerShared.h"
#include "../../Core/Debugger/SymbolMap.h"
#include "../../globals.h"
@ -262,7 +261,7 @@ void CtrlDisAsmView::parseDisasm(const char* disasm, char* opcode, char* argumen
disasm += 2;
for (int i = 0; i < 32; i++)
{
if (stricmp(jumpRegister+2,debugger->GetRegName(0,i)) == 0)
if (strcasecmp(jumpRegister+2,debugger->GetRegName(0,i)) == 0)
{
branchRegister = i;
break;

View File

@ -12,7 +12,6 @@
#include "../../Core/Debugger/SymbolMap.h"
#include "Debugger_Disasm.h"
#include "ExpressionParser.h"
#include "DebuggerShared.h"
#include "CtrlMemView.h"

View File

@ -13,7 +13,7 @@
#include "../../globals.h"
#include "Debugger_Disasm.h"
#include "ExpressionParser.h"
#include "DebuggerShared.h"
#include "../main.h"

View File

@ -0,0 +1,31 @@
#include "DebuggerShared.h"
#include "../InputBox.h"
bool parseExpression(char* exp, DebugInterface* cpu, u32& dest)
{
PostfixExpression postfix;
if (cpu->initExpression(exp,postfix) == false) return false;
return cpu->parseExpression(postfix,dest);
}
void displayExpressionError(HWND hwnd)
{
MessageBox(hwnd,getExpressionError(),"Invalid expression",MB_OK);
}
bool executeExpressionWindow(HWND hwnd, DebugInterface* cpu, u32& dest)
{
char expression[1024];
if (InputBox_GetString(GetModuleHandle(NULL), hwnd, "Expression", "",expression) == false)
{
return false;
}
if (parseExpression(expression,cpu,dest) == false)
{
displayExpressionError(hwnd);
return false;
}
return true;
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <Windows.h>
#include "..\..\Core\Debugger\DebugInterface.h"
enum { WM_DEB_RUNTOWPARAM = WM_USER+2,
WM_DEB_GOTOBREAKPOINT,
@ -7,4 +8,8 @@ enum { WM_DEB_RUNTOWPARAM = WM_USER+2,
WM_DEB_GOTOADDRESSEDIT,
WM_DEB_MAPLOADED,
WM_DEB_TABPRESSED
};
};
bool executeExpressionWindow(HWND hwnd, DebugInterface* cpu, u32& dest);
void displayExpressionError(HWND hwnd);
bool parseExpression(char* exp, DebugInterface* cpu, u32& dest);

View File

@ -10,7 +10,6 @@
#include "Debugger_MemoryDlg.h"
#include "Debugger_Disasm.h"
#include "Debugger_VFPUDlg.h"
#include "ExpressionParser.h"
#include "DebuggerShared.h"
#include "../main.h"
@ -580,7 +579,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
int regNum = -1;
for (int i = 0; i < 32; i++)
{
if (stricmp(reg+2,cpu->GetRegName(0,i)) == 0)
if (strcasecmp(reg+2,cpu->GetRegName(0,i)) == 0)
{
regNum = i;
break;

View File

@ -8,7 +8,6 @@
#include "Debugger_MemoryDlg.h"
#include "CtrlMemView.h"
#include "ExpressionParser.h"
#include "DebuggerShared.h"
#include "../../Core/MIPS/MIPSDebugInterface.h" // BAD

View File

@ -1,490 +0,0 @@
#include "ExpressionParser.h"
#include "../../Core/MIPS/MIPSDebugInterface.h"
#include "../InputBox.h"
typedef enum {
EXOP_BRACKETL, EXOP_BRACKETR, EXOP_SIGNPLUS, EXOP_SIGNMINUS,
EXOP_BITNOT, EXOP_LOGNOT, EXOP_MUL, EXOP_DIV, EXOP_MOD, EXOP_ADD, EXOP_SUB,
EXOP_SHL, EXOP_SHR, EXOP_GREATEREQUAL, EXOP_GREATER, EXOP_LOWEREQUAL, EXOP_LOWER,
EXOP_EQUAL, EXOP_NOTEQUAL, EXOP_BITAND, EXOP_XOR, EXOP_BITOR, EXOP_LOGAND,
EXOP_LOGOR, EXOP_TERTIF, EXOP_TERTELSE, EXOP_NUMBER, EXOP_NONE, EXOP_COUNT
} ExpressionOpcodeType;
typedef enum { EXCOMM_CONST, EXCOMM_OP } ExpressionCommand;
typedef std::pair<ExpressionCommand,u32> ExpressionPair;
static char expressionError[256];
typedef struct {
char Name[4];
unsigned char Priority;
unsigned char len;
unsigned char args;
bool sign;
} ExpressionOpcode;
const ExpressionOpcode ExpressionOpcodes[] = {
{ "(", 15, 1, 0, false }, // EXOP_BRACKETL
{ ")", 15, 1, 0, false }, // EXOP_BRACKETR
{ "+", 12, 1, 1, true }, // EXOP_SIGNPLUS
{ "-", 12, 1, 1, true }, // EXOP_SIGNMINUS
{ "~", 12, 1, 1, false }, // EXOP_BITNOT
{ "!", 12, 1, 1, false }, // EXOP_LOGNOT
{ "*", 11, 1, 2, false }, // EXOP_MUL
{ "/", 11, 1, 2, false }, // EXOP_DIV
{ "%", 11, 1, 2, false }, // EXOP_MOD
{ "+", 10, 1, 2, false }, // EXOP_ADD
{ "-", 10, 1, 2, false }, // EXOP_SUB
{ "<<", 9, 2, 2, false }, // EXOP_SHL
{ ">>", 9, 2, 2, false }, // EXOP_SHR
{ ">=", 8, 2, 2, false }, // EXOP_GREATEREQUAL
{ ">", 8, 1, 2, false }, // EXOP_GREATER
{ "<=", 8, 2, 2, false }, // EXOP_LOWEREQUAL
{ "<", 8, 1, 2, false }, // EXOP_LOWER
{ "==", 7, 2, 2, false }, // EXOP_EQUAL
{ "!=", 7, 2, 2, false }, // EXOP_NOTEQUAL
{ "&", 6, 1, 2, false }, // EXOP_BITAND
{ "^", 5, 1, 2, false }, // EXOP_XOR
{ "|", 4, 1, 2, false }, // EXOP_BITOR
{ "&&", 3, 2, 2, false }, // EXOP_LOGAND
{ "||", 2, 2, 2, false }, // EXOP_LOGOR
{ "?", 0, 1, 0, false }, // EXOP_TERTIF
{ ":", 1, 1, 3, false }, // EXOP_TERTELSE
{ "", 0, 0, 0, false }, // EXOP_NUMBER
{ "", 0, 0, 0, false } // EXOP_NONE
};
bool parseNumber(char* str, int defaultrad, int len, u32& result)
{
int val = 0;
int r = 0;
if (len == 0) len = (int) strlen(str);
if (str[0] == '0' && tolower(str[1]) == 'x')
{
r = 16;
str+=2;
len-=2;
} else if (str[0] == '$')
{
r = 16;
str++;
len--;
} else if (str[0] == '0' && tolower(str[1]) == 'o')
{
r = 8;
str+=2;
len-=2;
} else {
if (!(str[0] >= '0' && str[0] <= '9')) return false;
if (tolower(str[len-1]) == 'b')
{
r = 2;
len--;
} else if (tolower(str[len-1]) == 'o')
{
r = 8;
len--;
} else if (tolower(str[len-1]) == 'h')
{
r = 16;
len--;
} else {
r = defaultrad;
}
}
switch (r)
{
case 2: // bin
while (len--)
{
if (*str != '0' && *str != '1') return false;
val = val << 1;
if (*str++ == '1')
{
val++;
}
}
break;
case 8: // oct
while (len--)
{
if (*str < '0' || *str > '7') return false;
val = val << 3;
val+=(*str++-'0');
}
break;
case 10: // dec
while (len--)
{
if (*str < '0' || *str > '9') return false;
val = val * 10;
val += (*str++ - '0');
}
break;
case 16: // hex
while (len--)
{
char c = tolower(*str++);
if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) return false;
val = val << 4;
if (c >= 'a') val += c-'a'+10;
else val += c-'0';
}
break;
default:
return false;
}
result = val;
return true;
}
bool parseLabel(char* label, DebugInterface* debug, u32& dest)
{
// check if it's a register first
for (int i = 0; i < 32; i++)
{
char reg[8];
sprintf(reg,"r%d",i);
if (stricmp(label,reg) == 0
|| stricmp(label,debug->GetRegName(0,i)) == 0)
{
dest = debug->GetRegValue(0,i);
return true;
}
}
if (stricmp(label,"pc") == 0)
{
dest = debug->GetPC();
return true;
}
// now check labels
return debug->getSymbolValue(label,dest);
}
ExpressionOpcodeType getExpressionOpcode(char* str, int& ReturnLen, ExpressionOpcodeType LastOpcode)
{
int longestlen = 0;
ExpressionOpcodeType result = EXOP_NONE;
for (int i = 0; i < EXOP_NUMBER; i++)
{
if (ExpressionOpcodes[i].sign == true &&
(LastOpcode == EXOP_NUMBER || LastOpcode == EXOP_BRACKETR)) continue;
int len = ExpressionOpcodes[i].len;
if (len > longestlen)
{
if (strncmp(ExpressionOpcodes[i].Name,str,len) == 0)
{
result = (ExpressionOpcodeType) i;
longestlen = len;
}
}
}
ReturnLen = longestlen;
return result;
}
bool isAlphaNum(char c)
{
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c == '@' || c == '_' || c == '$')
{
return true;
} else {
return false;
}
}
bool parseExpression(char* infix, DebugInterface* cpu, u32& dest)
{
expressionError[0] = 0;
int infixPos = 0;
int infixLen = strlen(infix);
ExpressionOpcodeType lastOpcode = EXOP_NONE;
std::vector<ExpressionPair> postfixStack;
std::vector<ExpressionOpcodeType> opcodeStack;
while (infixPos < infixLen)
{
char first = tolower(infix[infixPos]);
char subStr[256];
int subPos = 0;
if (first == ' ' || first == '\t')
{
infixPos++;
continue;
}
if (first >= '0' && first <= '9')
{
while (isAlphaNum(infix[infixPos]))
{
subStr[subPos++] = infix[infixPos++];
}
subStr[subPos] = 0;
u32 value;
if (parseNumber(subStr,16,subPos,value) == false)
{
sprintf(expressionError,"Invalid number \"%s\"",subStr);
return false;
}
postfixStack.push_back(ExpressionPair(EXCOMM_CONST,value));
lastOpcode = EXOP_NUMBER;
} else if ((first >= 'a' && first <= 'z') || first == '@')
{
while (isAlphaNum(infix[infixPos]))
{
subStr[subPos++] = infix[infixPos++];
}
subStr[subPos] = 0;
u32 value;
if (parseLabel(subStr,cpu,value) == false)
{
sprintf(expressionError,"Invalid label \"%s\"",subStr);
return false;
}
postfixStack.push_back(ExpressionPair(EXCOMM_CONST,value));
lastOpcode = EXOP_NUMBER;
} else {
int len;
ExpressionOpcodeType type = getExpressionOpcode(&infix[infixPos],len,lastOpcode);
if (type == EXOP_NONE)
{
sprintf(expressionError,"Invalid operator at \"%s\"",&infix[infixPos]);
return false;
}
switch (type)
{
case EXOP_BRACKETL:
opcodeStack.push_back(type);
break;
case EXOP_BRACKETR:
while (true)
{
if (opcodeStack.empty())
{
sprintf(expressionError,"Closing parenthesis without opening one");
return false;
}
ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1];
opcodeStack.pop_back();
if (t == EXOP_BRACKETL) break;
postfixStack.push_back(ExpressionPair(EXCOMM_OP,t));
}
break;
default:
if (opcodeStack.empty() == false)
{
int CurrentPriority = ExpressionOpcodes[type].Priority;
while (!opcodeStack.empty())
{
ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1];
opcodeStack.pop_back();
if (t == EXOP_BRACKETL)
{
opcodeStack.push_back(t);
break;
}
if (ExpressionOpcodes[t].Priority >= CurrentPriority)
{
postfixStack.push_back(ExpressionPair(EXCOMM_OP,t));
} else {
opcodeStack.push_back(t);
break;
}
}
}
opcodeStack.push_back(type);
break;
}
infixPos += len;
lastOpcode = type;
}
}
while (!opcodeStack.empty())
{
ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1];
opcodeStack.pop_back();
if (t == EXOP_BRACKETL) // opening bracket without closing one
{
sprintf(expressionError,"Parenthesis not closed");
return false;
}
postfixStack.push_back(ExpressionPair(EXCOMM_OP,t));
}
// parse postfix now
int num = 0;
u32 opcode;
std::vector<u32> valueStack;
unsigned int arg[5];
while (num < postfixStack.size())
{
switch (postfixStack[num].first)
{
case EXCOMM_CONST: // konstante zahl
valueStack.push_back(postfixStack[num++].second);
break;
case EXCOMM_OP: // opcode
opcode = postfixStack[num++].second;
if (valueStack.size() < ExpressionOpcodes[opcode].args)
{
sprintf(expressionError,"Not enough arguments");
return false;
}
for (int l = 0; l < ExpressionOpcodes[opcode].args; l++)
{
arg[l] = valueStack[valueStack.size()-1];
valueStack.pop_back();
}
switch (opcode)
{
case EXOP_SIGNPLUS: // keine aktion nötig
break;
case EXOP_SIGNMINUS: // -0
valueStack.push_back(0-arg[0]);
break;
case EXOP_BITNOT: // ~b
valueStack.push_back(~arg[0]);
break;
case EXOP_LOGNOT: // !b
valueStack.push_back(!arg[0]);
break;
case EXOP_MUL: // a*b
valueStack.push_back(arg[1]*arg[0]);
break;
case EXOP_DIV: // a/b
if (arg[0] == 0)
{
sprintf(expressionError,"Division by zero");
return false;
}
valueStack.push_back(arg[1]/arg[0]);
break;
case EXOP_MOD: // a%b
if (arg[0] == 0)
{
sprintf(expressionError,"Modulo by zero");
return false;
}
valueStack.push_back(arg[1]%arg[0]);
break;
case EXOP_ADD: // a+b
valueStack.push_back(arg[1]+arg[0]);
break;
case EXOP_SUB: // a-b
valueStack.push_back(arg[1]-arg[0]);
break;
case EXOP_SHL: // a<<b
valueStack.push_back(arg[1]<<arg[0]);
break;
case EXOP_SHR: // a>>b
valueStack.push_back(arg[1]>>arg[0]);
break;
case EXOP_GREATEREQUAL: // a >= b
valueStack.push_back(arg[1]>=arg[0]);
break;
case EXOP_GREATER: // a > b
valueStack.push_back(arg[1]>arg[0]);
break;
case EXOP_LOWEREQUAL: // a <= b
valueStack.push_back(arg[1]<=arg[0]);
break;
case EXOP_LOWER: // a < b
valueStack.push_back(arg[1]<arg[0]);
break;
case EXOP_EQUAL: // a == b
valueStack.push_back(arg[1]==arg[0]);
break;
case EXOP_NOTEQUAL: // a != b
valueStack.push_back(arg[1]!=arg[0]);
break;
case EXOP_BITAND: // a&b
valueStack.push_back(arg[1]&arg[0]);
break;
case EXOP_XOR: // a^b
valueStack.push_back(arg[1]^arg[0]);
break;
case EXOP_BITOR: // a|b
valueStack.push_back(arg[1]|arg[0]);
break;
case EXOP_LOGAND: // a && b
valueStack.push_back(arg[1]&&arg[0]);
break;
case EXOP_LOGOR: // a && b
valueStack.push_back(arg[1]||arg[0]);
break;
case EXOP_TERTIF: // darf so nicht vorkommen
return false;
case EXOP_TERTELSE: // exp ? exp : exp, else muss zuerst kommen!
if (postfixStack[num++].second != EXOP_TERTIF)
{
sprintf(expressionError,"Invalid tertiary operator");
return false;
}
valueStack.push_back(arg[2]?arg[1]:arg[0]);
break;
}
break;
}
}
if (valueStack.size() != 1) return false;
dest = valueStack[0];
return true;
}
void displayExpressionError(HWND hwnd)
{
if (expressionError[0] == 0)
{
MessageBox(hwnd,"Invalid expression","Invalid expression",MB_OK);
} else {
MessageBox(hwnd,expressionError,"Invalid expression",MB_OK);
}
}
bool executeExpressionWindow(HWND hwnd, DebugInterface* cpu, u32& dest)
{
char expression[1024];
if (InputBox_GetString(GetModuleHandle(NULL), hwnd, "Expression", "",expression) == false)
{
return false;
}
if (parseExpression(expression,cpu,dest) == false)
{
displayExpressionError(hwnd);
return false;
}
return true;
}

View File

@ -1,8 +0,0 @@
#pragma once
#include "../../Common/CommonTypes.h"
class DebugInterface;
bool executeExpressionWindow(HWND hwnd, DebugInterface* cpu, u32& dest);
void displayExpressionError(HWND hwnd);
bool parseExpression(char* exp, DebugInterface* cpu, u32& dest);

View File

@ -263,10 +263,10 @@
<ClCompile Include="Debugger\CtrlDisAsmView.cpp" />
<ClCompile Include="Debugger\CtrlMemView.cpp" />
<ClCompile Include="Debugger\CtrlRegisterList.cpp" />
<ClCompile Include="Debugger\DebuggerShared.cpp" />
<ClCompile Include="Debugger\Debugger_Disasm.cpp" />
<ClCompile Include="Debugger\Debugger_MemoryDlg.cpp" />
<ClCompile Include="Debugger\Debugger_VFPUDlg.cpp" />
<ClCompile Include="Debugger\ExpressionParser.cpp" />
<ClCompile Include="DinputDevice.cpp" />
<ClCompile Include="EmuThread.cpp" />
<ClCompile Include="InputDevice.cpp" />
@ -306,7 +306,6 @@
<ClInclude Include="Debugger\Debugger_Disasm.h" />
<ClInclude Include="Debugger\Debugger_MemoryDlg.h" />
<ClInclude Include="Debugger\Debugger_VFPUDlg.h" />
<ClInclude Include="Debugger\ExpressionParser.h" />
<ClInclude Include="DinputDevice.h" />
<ClInclude Include="EmuThread.h" />
<ClInclude Include="InputDevice.h" />

View File

@ -101,7 +101,7 @@
<ClCompile Include="ControlMapping.cpp">
<Filter>Windows\Input</Filter>
</ClCompile>
<ClCompile Include="Debugger\ExpressionParser.cpp">
<ClCompile Include="Debugger\DebuggerShared.cpp">
<Filter>Windows\Debugger</Filter>
</ClCompile>
</ItemGroup>
@ -182,9 +182,6 @@
<ClInclude Include="ControlMapping.h">
<Filter>Windows\Input</Filter>
</ClInclude>
<ClInclude Include="Debugger\ExpressionParser.h">
<Filter>Windows\Debugger</Filter>
</ClInclude>
<ClInclude Include="Debugger\DebuggerShared.h">
<Filter>Windows\Debugger</Filter>
</ClInclude>

2
native

@ -1 +1 @@
Subproject commit 5127d746333e125193dd68c253ffcb96446c13b9
Subproject commit 42877f5a1fd94a95a4a3df5d47abeae96d6f9ca9