Code Monkey home page Code Monkey logo

nutcracker's People

Contributors

darknesswind avatar psimage avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

nutcracker's Issues

do/while loop detection is faulty

If a while loop contains a continue statement, NutCracker may misinterpret the loop as a do/while loop. For example, take this code:

local i,x
while(true)
{
  if(x) continue;
  i++;
}

If you compile and decompile it, you get this:

do
{
	local i;
	local x;
	// [002]  OP_JZ             4      4    0    0
}
while (x);
i++;

The problem is that PreprocessDoWhileInfo sees the backwards jump from the conditional continue statement and thinks that's the end of a do/while loop and it gets decompiled that way even though later instructions wouldn't make sense if it was a do/while loop.

The right fix is probably to rewrite NutCracker to do a proper basic block analysis rather than just looking for patterns in the instruction stream, but a hacky fix could be to abort do/while loop decompilation and retry as a non-do/while loop if NutCracker gets into a situation it doesn't understand.

do/while loop includes an instruction before the loop

Compile this code:

i = 1
do j = 0 while (j)

Then decompile it with NutCracker. The result is:

do
{
        this.i = 1;
        this.j = 0;
}
while (this.j);

In general, one operation before a do/while loop is included in the loop. I believe the reason is that the beginning of the loop is calculated incorrectly. In PreprocessDoWhileInfo there's this line:

int beginPos = endPos + curInst.arg1;

I think it should be replaced with:

int beginPos = endPos + 1 + curInst.arg1;

weird error in foreach loop

Try compiling this function.

function foo() {
  foreach(o in list) {
    if (a) { continue; }
    b.push();
  }
}

Then decompile it. You get this result:

function foo() {
  foreach(o in list) {
    if (a) { continue; }
    a.push();
  }
}

In fact, whatever the condition of the 'if' is will be duplicated on the next line. E.g.

if (a.query(1,2,3)) { continue; }
b.push();

becomes:

if (a.query(1,2,3)) { continue; }
a.query(1,2,3).push();

when it's placed inside the foreach loop.

loop flags are not reset in nested loops, causing bad output

When new state blocks are pushed onto the state stack, the loop state is not reset. This causes failures processing nested loops. Try compiling and decompiling this code:

while(true)
{
	if (true)
	{
		if (true)
		{
				continue;
		}
	}

	for(local i = 0; i <= 10; i++)
	{
		if (true)
		{
				continue;
		}
		else if (true)
		{
			if (true)
			{
			}
			else
			{
			}
		}
	}
}

The output is:

while (true)
{
	if (true)
	{
		if (true)
		{
			continue;
		}
	}

	local i = 0;

	while (i <= 10)
	{
		if (true)
		{
			continue;
		}
		else if (true)
		{
			if (true)
			{
			}
			else
			{
			}
		}

		i++;
	}
}

The 'for' loop has changed into an infinite loop. I think when pushing states onto the state stack, the loop flags should be cleared:

// While block found - push loop block
BlockState prevBlockState = state.m_BlockState;
state.m_BlockState.inLoop = BlockState::WhileLoop;
state.m_BlockState.loopFlags = 0; // CLEAR THE LOOP FLAGS

Support all statements that use OP_JCMP

OP_JCMP was added in 3.x as an optimization for single comparison expressions instead of using OP_CMP and conditional jump (OP_JZ is the only one in 3.x?).
Currently those statements are not recognized correctly when OP_JCMP is used:

  1. If/IfElse
    image
  2. While/DoWhile
    image

'break' is not emitted in some cases

Consider this small script:

while(true) {
  if(!this.condition) {
    if(--n <= 0) { break; }
  }
}

When compiled and decompiled, the output is:

while (true) {
        if (!this.condition) {
                if (--this.n <= 0) {
                }
        }
}

The 'break' statement is missing, resulting in an infinite loop.

floating-point precision is lost (and it's also ugly)

NutCracker loses precision when outputting floating point numbers. For example, the script print(1e-16); is decompiled as this.print(0.00000000); which is clearly not the same thing. The script print(0.9); is decompiled as this.print(0.89999998); which might be the same thing but also might not be, and definitely looks bad.

Also, the output is ugly. print(1.0); is decompiled as this.print(1.00000000);. This gets especially noticeable with math-heavy code.

Basically, the floating-point number printer should be fixed to print in a round-trippable format that's no longer than necessary to convey the correct value.

NutCracker is too eager to replace while loops with for loops

In DecompileLoopJumpInstruction, if the jump is backwards to the start of the loop it may decompile it as a continue statement, but it doesn't set the UsedBackwardJumpContinue flag, so the postprocessing step may convert it to a for loop improperly. See this code:

local i,x
while(true)
{
  if(x) continue;
  i++;
}

When compiled and decompiled (assuming some other bugs are fixed), the result is:

local i;
for( local x; true; i++ )
{
	if (x)
	{
		continue;
	}
}

This is not equivalent. In the first example, the increment is skipped if x is true but in the second example it is not.

missing break; causes assertion

The OP_NEWOBJ opcode is missing a break statement in the switch:

case NOT_CLASS:
{
	ExpressionPtr attributes;
	ExpressionPtr baseClass;

	if (arg1 != -1)
		baseClass = state.GetVar(arg1);

	if (arg2 != 0xff)
		attributes = state.GetVar(arg2);

	state.SetVar(arg0, ExpressionPtr(new NewClassExpression(baseClass, attributes)));
    // MISSING BREAK HERE!
}
default:
	assert(0);
	break;

This causes a crash when it falls into the assert(0).

For loop postprocessing can drop the increment expression (?)

It looks like the for loop postprocessing code can drop the increment statement in one case:

StatementPtr incrementStatement = GetForIncrementStatement();
if ((prevStatement->GetType() != Stat_LocalVar) && !incrementStatement)
    return  StatementPtr();

GetForIncrementStatement actually removes the increment statement from the block, and then if it doesn't get used (because GetType() == Stat_LocalVar), I believe it just disappears.

edit font.

Good time of day.
How do I change the font location?
There are no values in nut files.

Mods to decompile wide strings (unicode)

I was looking at some .nut file and found NutCracker, as its advised on the readme, it doesn't work, luckily i have some experience on lua so i quickly fixed it to dump the files.

First thing to add NutScript.cpp > LoadFromStream
if (reader.ReadInt32() != sizeof(uint16_t))...
using wide string you will use 2 bytes for each char

BinaryParser.h > ReadSQString

		static char* buff;
		static std::vector<char> buf;
		int len = ReadInt32()*2;
		if (len < 0)
			len = 0;
		if (buf.size() < (size_t)len)
			buf.resize(len);
		buff = (char*)malloc(len/2);
		Read((void*)buf.data(), len, true);
		WideCharToMultiByte(/*CP_ACP*/0, 0, (wchar_t*)buf.data(), len, buff, len/2, 0, 0);
		str.assign(buff, len/2);
		delete buff;

dirty code, note len is multiplied by 2 and then WideCharToMultiByte is used to convert text to ansi representation. You should include windows.h on BinaryParser.h in order to use encoding api.

floating point output is wrong in some cultures

NutCracker uses the .OCP culture, which prints numbers according to the user's locale. So in countries that use the comma as the decimal separator rather than the period, a number like 1.25 gets printed as 1,250000. Then, NutCracker tries to find a period in it:

if (m_text.indexOf('.') == std::string::npos)
    m_text.append(L".0");

Since it doesn't find one, it adds ".0" to the end, resulting in "1,250000.0", which is not a valid Squirrel number.

NutCracker should use a fixed culture that always uses the period as the decimal separator.

Accessing non valid stack position?

Sorry to trouble you, but every time I try to decompile this particular cnut, I keep getting the "accessing non valid stack position" error. I have tried to debug the code, and it looks like the BinaryReader is mangling some of the int32 reads, but I'm not advanced enough with C++ to be able to figure out why.

If you're still working at all on this repo, could you please take a look? Would greatly appreciate it.

global.zip

Crash when generating DoWhileStatement

In some cases void NutFunction::DecompileDoWhileLoop( VMState& state, int endPos) const endPos > state.IP() which causes ExpressionPtr condition; to be null and crash later when printing DoWhileStatement.

Tested on: script.zip

Crash when generating DoWhileStatement

Here is another sample that causes NutCracker to crash: script.zip
Now it's something to do with foreach being inside a loop. At least that is what a quick look in debugger gives.
It starts decompiling statements inside a loop and goes outside that loop when processing OP_FOREACH instruction in that sample (foreach loopEndIp ends up being higher that the end of a parent loop).
Maybe FOREACH loop decompiling is not up-to-date with the recent changes to loops or determination of a loop block is incorrect in some cases.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.