Skip to content

Commit d4bc1ef

Browse files
committed
Optimize assign() to __unsafe_assign__ ()
Optimize `assign()` to `__unsafe_assign__ ()` when it is known that the `assign()` typecheck will always pass.
1 parent 082f45b commit d4bc1ef

File tree

6 files changed

+121
-25
lines changed

6 files changed

+121
-25
lines changed

src/main/java/com/laytonsmith/core/constructs/IVariableList.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ public void set(IVariable v) {
7979
varList.put(v.getVariableName(), v);
8080
}
8181

82+
public IVariable get(String name) {
83+
return varList.get(name);
84+
}
85+
8286
public IVariable get(String name, Target t, boolean bypassAssignedCheck, Environment env) {
8387
IVariable v = varList.get(name);
8488
if(v == null) {

src/main/java/com/laytonsmith/core/functions/Compiler.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
import com.laytonsmith.core.constructs.CVoid;
3333
import com.laytonsmith.core.constructs.Construct;
3434
import com.laytonsmith.core.constructs.IVariable;
35+
import com.laytonsmith.core.constructs.IVariableList;
3536
import com.laytonsmith.core.constructs.InstanceofUtil;
3637
import com.laytonsmith.core.constructs.Target;
3738
import com.laytonsmith.core.constructs.Token;
3839
import com.laytonsmith.core.environments.Environment;
40+
import com.laytonsmith.core.environments.GlobalEnv;
3941
import com.laytonsmith.core.environments.Environment.EnvironmentImpl;
4042
import com.laytonsmith.core.exceptions.CRE.CRECastException;
4143
import com.laytonsmith.core.exceptions.CRE.CRENotFoundException;
@@ -1205,6 +1207,7 @@ public ParseTree postParseRewrite(ParseTree ast, Environment env,
12051207
}
12061208
}
12071209
}
1210+
12081211
@api
12091212
@noprofile
12101213
@hide("This is only used internally by the compiler.")
@@ -1325,4 +1328,54 @@ public ParseTree optimizeDynamic(Target t, Environment env, Set<Class<? extends
13251328
return null;
13261329
}
13271330
}
1331+
1332+
@api
1333+
@noprofile
1334+
@noboilerplate
1335+
@hide("This is only used internally by the compiler.")
1336+
public static class __unsafe_assign__ extends assign {
1337+
1338+
public static final String NAME = "__unsafe_assign__";
1339+
1340+
@Override
1341+
public String getName() {
1342+
return NAME;
1343+
}
1344+
1345+
@Override
1346+
public Mixed exec(Target t, Environment env, Mixed... args) throws CancelCommandException, ConfigRuntimeException {
1347+
1348+
// Get arguments.
1349+
CClassType type;
1350+
String varName;
1351+
Mixed val;
1352+
if(args.length == 3) {
1353+
type = (CClassType) args[0];
1354+
varName = ((IVariable) args[1]).getVariableName();
1355+
val = args[2];
1356+
} else {
1357+
type = null;
1358+
varName = ((IVariable) args[0]).getVariableName();
1359+
val = args[1];
1360+
}
1361+
1362+
// Get variable list.
1363+
IVariableList list = env.getEnv(GlobalEnv.class).GetVarList();
1364+
1365+
// Unwrap assigned value from IVariable if an IVariable is passed.
1366+
if(val instanceof IVariable ivar) {
1367+
val = list.get(ivar.getVariableName(), ivar.getTarget(), env).ival();
1368+
}
1369+
1370+
// Assign value to variable.
1371+
IVariable var = list.get(varName);
1372+
if(var == null) {
1373+
var = new IVariable(type, varName, val, t);
1374+
list.set(var);
1375+
} else {
1376+
var.setIval(val);
1377+
}
1378+
return var;
1379+
}
1380+
}
13281381
}

src/main/java/com/laytonsmith/core/functions/DataHandling.java

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,10 @@
9696
import com.laytonsmith.core.functions.ArrayHandling.array_push;
9797
import com.laytonsmith.core.functions.ArrayHandling.array_set;
9898
import com.laytonsmith.core.functions.Compiler.__autoconcat__;
99+
import com.laytonsmith.core.functions.Compiler.__cast__;
99100
import com.laytonsmith.core.functions.Compiler.__statements__;
100101
import com.laytonsmith.core.functions.Compiler.__type_ref__;
102+
import com.laytonsmith.core.functions.Compiler.__unsafe_assign__;
101103
import com.laytonsmith.core.functions.Compiler.centry;
102104
import com.laytonsmith.core.natives.interfaces.Mixed;
103105
import com.laytonsmith.tools.docgen.templates.ArrayIteration;
@@ -637,6 +639,8 @@ public ParseTree optimizeDynamic(Target t, Environment env,
637639
if(children.size() < 2) {
638640
return null;
639641
}
642+
643+
// Generate warning for assigning a variable to itself.
640644
if(children.get(0).getData() instanceof IVariable
641645
&& children.get(1).getData() instanceof IVariable) {
642646
if(((IVariable) children.get(0).getData()).getVariableName().equals(
@@ -646,18 +650,49 @@ public ParseTree optimizeDynamic(Target t, Environment env,
646650
new CompilerWarning(msg, t, null));
647651
}
648652
}
649-
{
650-
// Check for declaration of variables named "pass" or "password" and see if it's defined as a
651-
// secure_string. If not, warn.
652-
if(children.get(0).getData() instanceof CClassType && children.get(1).getData() instanceof IVariable) {
653-
boolean isString
654-
= ((CClassType) children.get(0).getData()).getNativeType() == CString.class;
655-
String varName = ((IVariable) children.get(1).getData()).getVariableName();
656-
if((varName.equalsIgnoreCase("pass") || varName.equalsIgnoreCase("password"))
657-
&& isString) {
658-
String msg = "";
659-
env.getEnv(CompilerEnvironment.class).addCompilerWarning(fileOptions,
660-
new CompilerWarning(msg, t, FileOptions.SuppressWarning.CodeUpgradeNotices));
653+
654+
// Check for declaration of variables named "pass" or "password" and see if it's defined as a
655+
// secure_string. If not, warn.
656+
if(children.get(0).getData() instanceof CClassType && children.get(1).getData() instanceof IVariable) {
657+
boolean isString
658+
= ((CClassType) children.get(0).getData()).getNativeType() == CString.class;
659+
String varName = ((IVariable) children.get(1).getData()).getVariableName();
660+
if((varName.equalsIgnoreCase("pass") || varName.equalsIgnoreCase("password"))
661+
&& isString) {
662+
String msg = "";
663+
env.getEnv(CompilerEnvironment.class).addCompilerWarning(fileOptions,
664+
new CompilerWarning(msg, t, FileOptions.SuppressWarning.CodeUpgradeNotices));
665+
}
666+
}
667+
668+
// Rewrite "assign(type, var, val)" to "__unsafe_assign__(type, var, val)" if the typecheck cannot fail.
669+
if(children.size() == 3) {
670+
ParseTree typeNode = children.get(0);
671+
ParseTree varNode = children.get(1);
672+
ParseTree valNode = children.get(2);
673+
if(typeNode.getData() instanceof CClassType declaredType
674+
&& varNode.getData() instanceof IVariable) {
675+
if(valNode.isConst()) {
676+
CClassType valType = valNode.getData().typeof();
677+
if((valType != CClassType.AUTO || declaredType == CClassType.AUTO)
678+
&& InstanceofUtil.isInstanceof(valType, declaredType, env)) {
679+
ParseTree newAssignNode = new ParseTree(new CFunction(__unsafe_assign__.NAME, t), fileOptions);
680+
newAssignNode.addChild(typeNode);
681+
newAssignNode.addChild(varNode);
682+
newAssignNode.addChild(valNode);
683+
return newAssignNode;
684+
}
685+
}
686+
if(valNode.getData() instanceof CFunction cf && cf.getCachedFunction() != null
687+
&& cf.getCachedFunction().getName().equals(__cast__.NAME) && valNode.numberOfChildren() == 2
688+
&& valNode.getChildAt(1).getData() instanceof CClassType castType
689+
&& (castType != CClassType.AUTO || declaredType == CClassType.AUTO)
690+
&& InstanceofUtil.isInstanceof(castType, declaredType, env)) {
691+
ParseTree newAssignNode = new ParseTree(new CFunction(__unsafe_assign__.NAME, t), fileOptions);
692+
newAssignNode.addChild(typeNode);
693+
newAssignNode.addChild(varNode);
694+
newAssignNode.addChild(valNode);
695+
return newAssignNode;
661696
}
662697
}
663698
}
@@ -1505,13 +1540,14 @@ public static Procedure getProcedure(Target t, Environment env, Script parent, P
15051540
} else {
15061541
boolean thisNodeIsAssign = false;
15071542
if(nodes[i].getData() instanceof CFunction) {
1508-
if((nodes[i].getData()).val().equals(assign.NAME)) {
1543+
String funcName = nodes[i].getData().val();
1544+
if(funcName.equals(assign.NAME) || funcName.equals(__unsafe_assign__.NAME)) {
15091545
thisNodeIsAssign = true;
15101546
if((nodes[i].getChildren().size() == 3 && Construct.IsDynamicHelper(nodes[i].getChildAt(0).getData()))
15111547
|| Construct.IsDynamicHelper(nodes[i].getChildAt(1).getData())) {
15121548
usesAssign = true;
15131549
}
1514-
} else if((nodes[i].getData()).val().equals(__autoconcat__.NAME)) {
1550+
} else if(funcName.equals(__autoconcat__.NAME)) {
15151551
throw new CREInvalidProcedureException("Invalid arguments defined for procedure", t);
15161552
}
15171553
}

src/main/java/com/laytonsmith/core/functions/Exceptions.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,10 @@ public ParseTree optimizeDynamic(Target t, Environment env,
618618
}
619619

620620
// Validate that the node is an assign with 3 arguments.
621-
if(!CFunction.IsFunction(assign, DataHandling.assign.class) || assign.numberOfChildren() != 3) {
621+
if(!(assign.getData() instanceof CFunction cf && cf.getFunction() != null
622+
&& (cf.getFunction().getName().equals(DataHandling.assign.NAME)
623+
|| cf.getFunction().getName().equals(Compiler.__unsafe_assign__.NAME)))
624+
|| assign.numberOfChildren() != 3) {
622625
throw new ConfigCompileException("Expecting a variable declaration, but instead "
623626
+ assign.getData().val() + " was found", t);
624627
}

src/test/java/com/laytonsmith/core/NewExceptionHandlingTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public void setUp() {
4545

4646
@Test
4747
public void testBasicKeywordUsage() throws Exception {
48-
assertEquals("__statements__(complex_try(__statements__(noop()),assign(ms.lang.IOException,@e,null),__statements__(noop()),"
49-
+ "assign(ms.lang.Exception,@e,null),__statements__(noop()),__statements__(noop())))",
48+
assertEquals("__statements__(complex_try(__statements__(noop()),__unsafe_assign__(ms.lang.IOException,@e,null),__statements__(noop()),"
49+
+ "__unsafe_assign__(ms.lang.Exception,@e,null),__statements__(noop()),__statements__(noop())))",
5050
optimize("try { } catch (IOException @e){ } catch (Exception @e){ } finally { }"));
5151
}
5252

src/test/java/com/laytonsmith/core/OptimizationTest.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ public void testAssignWithComplexSymbols() throws Exception {
264264

265265
@Test
266266
public void testMultipleAdjacentAssignment() throws Exception {
267-
assertEquals("__statements__(assign(@one,inc(@two)),assign(ms.lang.int,@three,0),assign(@four,'test'))",
267+
assertEquals("__statements__(assign(@one,inc(@two)),__unsafe_assign__(ms.lang.int,@three,0),assign(@four,'test'))",
268268
optimize("@one = ++@two; int @three = 0; @four = 'test';"));
269269
}
270270

@@ -584,9 +584,9 @@ public void testIfStatementWithMultipleInvalidParameters() throws Exception {
584584

585585
@Test
586586
public void testSconcatWithNonStatement() throws Exception {
587-
assertEquals("__statements__(assign(ms.lang.int,@i,0),assign(ms.lang.int,@j,0))",
587+
assertEquals("__statements__(__unsafe_assign__(ms.lang.int,@i,0),__unsafe_assign__(ms.lang.int,@j,0))",
588588
optimize("int @i = 0; int @j = 0;"));
589-
assertEquals("sconcat(__statements__(assign(ms.lang.int,@i,0)),assign(ms.lang.int,@j,0))",
589+
assertEquals("sconcat(__statements__(__unsafe_assign__(ms.lang.int,@i,0)),__unsafe_assign__(ms.lang.int,@j,0))",
590590
optimize("int @i = 0; int @j = 0"));
591591
}
592592

@@ -673,13 +673,13 @@ public void testParenthesisRewritesCorrectly1() throws Exception {
673673

674674
@Test
675675
public void testParenthesisRewritesCorrectly2() throws Exception {
676-
assertEquals("__statements__(assign(ms.lang.closure,@c,closure(assign(ms.lang.int,@a,null),__statements__(msg(@a)))),execute(10,@c))",
676+
assertEquals("__statements__(assign(ms.lang.closure,@c,closure(__unsafe_assign__(ms.lang.int,@a,null),__statements__(msg(@a)))),execute(10,@c))",
677677
optimize("closure @c = closure(int @a) {msg(@a);};\n@c(10);"));
678678
}
679679

680680
@Test
681681
public void testParenthesisRewritesCorrectly3() throws Exception {
682-
assertEquals("__statements__(assign(ms.lang.closure,@c,closure(assign(ms.lang.int,@a,null),assign(ms.lang.int,@b,null),__statements__(msg(@a)))),execute(10,11,@c))",
682+
assertEquals("__statements__(assign(ms.lang.closure,@c,closure(__unsafe_assign__(ms.lang.int,@a,null),__unsafe_assign__(ms.lang.int,@b,null),__statements__(msg(@a)))),execute(10,11,@c))",
683683
optimize("closure @c = closure(int @a, int @b) {msg(@a);};\n@c(10,11);"));
684684
}
685685

@@ -728,7 +728,7 @@ public void testNoErrorWithParenthesisAfterSymbol() throws Exception {
728728

729729
@Test
730730
public void testProcReference() throws Exception {
731-
assertEquals("__statements__(proc(ms.lang.int,'_asdf',assign(ms.lang.int,@i,null),__statements__(return(@i))),"
731+
assertEquals("__statements__(proc(ms.lang.int,'_asdf',__unsafe_assign__(ms.lang.int,@i,null),__statements__(return(@i))),"
732732
+ "assign(ms.lang.Callable,@c,get_proc('_asdf')))",
733733
optimize("int proc _asdf(int @i) { return @i; } Callable @c = proc _asdf;"));
734734
}
@@ -761,9 +761,9 @@ public void testSmartStringToDumbStringRewriteWithEscapes() throws Exception {
761761

762762
@Test
763763
public void testForIsSelfStatement() throws Exception {
764-
assertEquals("__statements__(for(assign(ms.lang.int,@i,0),lt(@i,10),inc(@i),__statements__(msg(@i))))",
764+
assertEquals("__statements__(for(__unsafe_assign__(ms.lang.int,@i,0),lt(@i,10),inc(@i),__statements__(msg(@i))))",
765765
optimize("for(int @i = 0, @i < 10, @i++) { msg(@i); }"));
766-
assertEquals("__statements__(while(true,__statements__(msg(''),for(assign(ms.lang.int,@i,0),lt(@i,10),inc(@i),__statements__(msg(@i))))))",
766+
assertEquals("__statements__(while(true,__statements__(msg(''),for(__unsafe_assign__(ms.lang.int,@i,0),lt(@i,10),inc(@i),__statements__(msg(@i))))))",
767767
optimize("while(true) { msg('') for(int @i = 0, @i < 10, @i++) { msg(@i); }}"));
768768
}
769769

0 commit comments

Comments
 (0)