Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support max hex/oct/bin literals + Fix uncaught exceptions when providing too large hex/oct/bin literals #1392

Merged
merged 3 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/main/java/com/laytonsmith/core/MethodScriptCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -1763,7 +1763,12 @@ public static ParseTree compile(TokenStream stream, Environment environment,
}
continue;
} else if(t.type == TType.LIT) {
Construct c = Static.resolveConstruct(t.val(), t.target, true);
Construct c;
try {
c = Static.resolveConstruct(t.val(), t.target, true);
} catch (ConfigRuntimeException ex) {
throw new ConfigCompileException(ex);
}
if((c instanceof CInt || c instanceof CDecimal) && next1.type == TType.DOT && next2.type == TType.LIT) {
// make CDouble/CDecimal here because otherwise Long.parseLong() will remove
// minus zero before decimals and leading zeroes after decimals
Expand Down
41 changes: 36 additions & 5 deletions src/main/java/com/laytonsmith/core/Static.java
Original file line number Diff line number Diff line change
Expand Up @@ -592,21 +592,52 @@ public static Construct resolveConstruct(String val, Target t, boolean returnBar
throw new CREFormatException("Hex numbers must only contain digits 0-9, and the letters A-F, but \"" + val + "\" was found.", t);
}
if(VALID_HEX.matcher(val).matches()) {
//Hex number
return new CInt(Long.parseLong(val.substring(2), 16), t);

// Parse hex number. Special handling for 16-digit numbers to support setting all 64 bits of the value.
long longVal;
if(val.length() > 16 + 2) {
throw new CREFormatException("Hex numbers must contain at most 16 digits, but \"" + val + "\" was found.", t);
} else if(val.length() == 16 + 2) {
longVal = (Long.parseLong(val.substring(2, 10), 16) << 32) | Long.parseLong(val.substring(10, 18), 16);
} else {
longVal = Long.parseLong(val.substring(2), 16);
}
return new CInt(longVal, t);
}
if(INVALID_BINARY.matcher(val).matches()) {
throw new CREFormatException("Binary numbers must only contain digits 0 and 1, but \"" + val + "\" was found.", t);
}
if(VALID_BINARY.matcher(val).matches()) {
//Binary number
return new CInt(Long.parseLong(val.substring(2), 2), t);

// Parse binary number. Special handling for 64-digit numbers to support setting all 64 bits of the value.
long longVal;
if(val.length() > 64 + 2) {
throw new CREFormatException("Binary numbers must contain at most 64 digits, but \"" + val + "\" was found.", t);
} else if(val.length() == 64 + 2) {
longVal = (Long.parseLong(val.substring(2, 34), 2) << 32) | Long.parseLong(val.substring(34, 66), 2);
} else {
longVal = Long.parseLong(val.substring(2), 2);
}
return new CInt(longVal, t);
}
if(INVALID_OCTAL.matcher(val).matches()) {
throw new CREFormatException("Octal numbers must only contain digits 0-7, but \"" + val + "\" was found.", t);
}
if(VALID_OCTAL.matcher(val).matches()) {
return new CInt(Long.parseLong(val.substring(2), 8), t);

// Parse octal number. Special handling for 8-digit numbers to support setting all 64 bits of the value.
long longVal;
if(val.length() > 22 + 2) {
throw new CREFormatException("Octal numbers must contain at most 22 digits, but \"" + val + "\" was found.", t);
} else if(val.length() == 22 + 2) {
if(val.charAt(2) != '1') {
throw new CREFormatException("Octal number exceeds maximum 64-bit value 0o1777777777777777777777. Found \"" + val + "\".", t);
}
longVal = Long.parseLong(val.substring(3), 8) | (1L << 63);
} else {
longVal = Long.parseLong(val.substring(2), 8);
}
return new CInt(longVal, t);
}
if(INVALID_DECIMAL.matcher(val).matches()) {
throw new CREFormatException("Decimal numbers must only contain digits, but \"" + val + "\" was found.", t);
Expand Down
11 changes: 5 additions & 6 deletions src/test/java/com/laytonsmith/core/MethodScriptCompilerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.laytonsmith.core.environments.CommandHelperEnvironment;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.exceptions.AbstractCompileException;
import com.laytonsmith.core.exceptions.CRE.CREFormatException;
import com.laytonsmith.core.exceptions.ConfigCompileException;
import com.laytonsmith.core.exceptions.ConfigCompileGroupException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
Expand Down Expand Up @@ -239,15 +238,15 @@ public void testExecute3() {
= "[";
MethodScriptCompiler.execute(MethodScriptCompiler.compile(MethodScriptCompiler.lex(script, null, null, true), null, envs), env, null, null);
fail("Test passed, but wasn't supposed to");
} catch (ConfigCompileException | ConfigCompileGroupException ex) {
} catch (AbstractCompileException ex) {
//Passed
}
try {
String script
= "]";
MethodScriptCompiler.execute(MethodScriptCompiler.compile(MethodScriptCompiler.lex(script, null, null, true), null, envs), env, null, null);
fail("Test passed, but wasn't supposed to");
} catch (ConfigCompileException | ConfigCompileGroupException ex) {
} catch (AbstractCompileException ex) {
//Passed
}
}
Expand Down Expand Up @@ -451,7 +450,7 @@ public void testCompile1() {
String config = "/cmd [$p] $q = msg('')";
MethodScriptCompiler.preprocess(MethodScriptCompiler.lex(config, null, null, false), envs).get(0).compile(env);
fail("Test passed, but wasn't supposed to");
} catch (ConfigCompileException | ConfigCompileGroupException ex) {
} catch (AbstractCompileException ex) {
//Passed
}
}
Expand Down Expand Up @@ -496,7 +495,7 @@ public void testCompile2() {
String config = "/cmd [$p=player()] = msg('')";
MethodScriptCompiler.preprocess(MethodScriptCompiler.lex(config, null, null, false), envs).get(0).compile(env);
fail("Test passed, but wasn't supposed to");
} catch (ConfigCompileException | ConfigCompileGroupException ex) {
} catch (AbstractCompileException ex) {
//Passed
}
}
Expand Down Expand Up @@ -1019,7 +1018,7 @@ public void testLiteralDecimal() throws Exception {
assertEquals("ms.lang.decimal", SRun("typeof(0m1234567890987654321.1234567890987654321)", fakePlayer));
}

@Test(expected = CREFormatException.class)
@Test(expected = AbstractCompileException.class)
public void testLiteralBinary() throws Exception {
SRun("0b2", fakePlayer);
}
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/com/laytonsmith/core/TestStatic.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,22 @@ public void testResolveConstruct() {
assertTrue(Static.resolveConstruct("1.1", Target.UNKNOWN) instanceof CDouble);
assertTrue(Static.resolveConstruct("astring", Target.UNKNOWN) instanceof CString);
assertTrue(Static.resolveConstruct("string", Target.UNKNOWN) instanceof CClassType);
assertTrue(getResolveConstructLong("0xFF") == 0xFF);
assertTrue(getResolveConstructLong("0xABCDEF0123456789") == 0xABCDEF0123456789L); // All chars.
assertTrue(getResolveConstructLong("0xFFAFFFFFFFF0FFFF") == 0xFFAFFFFFFFF0FFFFL);
assertTrue(getResolveConstructLong("0xFFFFFFFFFFFFFFFF") == 0xFFFFFFFFFFFFFFFFL); // Max value.
assertTrue(getResolveConstructLong("0b100") == 0b100);
assertTrue(getResolveConstructLong("0b1111011111111011111111111011111111111111111111110111111111111110")
== 0b1111011111111011111111111011111111111111111111110111111111111110L);
assertTrue(getResolveConstructLong("0b1111111111111111111111111111111111111111111111111111111111111111")
== 0b1111111111111111111111111111111111111111111111111111111111111111L); // Max value.
assertTrue(getResolveConstructLong("0o76543210") == 076543210L); // All chars.
assertTrue(getResolveConstructLong("0o1737745677477125767277") == 01737745677477125767277L);
assertTrue(getResolveConstructLong("0o1777777777777777777777") == 01777777777777777777777L); // Max value.
}

private static long getResolveConstructLong(String val) {
return ((CInt) Static.resolveConstruct(val, Target.UNKNOWN)).getInt();
}

}