Skip to content
Permalink
Browse files
Fix NPEs in CClassType (#1208)
* Fix NPEs in CClassType

The CClassType invalidType is cached in a thread-unsafe way. This causes problems when LangServ is performing an operation on the same CClassType, since one thread can start using the Mixed[1] created by the other thread before it is populated with data, getting null when reading its first element.

* Switch LangServ back to cmdline mode

LangServ is currently running in EMBEDDED mode, which is a refactoring mistake. This commit changes it back to CMDLINE mode.
  • Loading branch information
Pieter12345 committed Jun 10, 2020
1 parent d8def74 commit 067118a0126eca4f07f333a2d8ee983e1f80e0ce
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 34 deletions.
@@ -267,32 +267,39 @@ private CClassType(FullyQualifiedClassName type, Target t, boolean newDefinition
* at least no way this will throw a ClassCastException, so given that, we are able to supress that exception here.
*/
private void instantiateInvalidType(Environment env) {
if(invalidType != UNINITIALIZED) {
if(this.invalidType != UNINITIALIZED) {
return;
}
@SuppressWarnings("LocalVariableHidesMemberVariable")
String fqcn = this.fqcn.getFQCN();
try {
if("auto".equals(fqcn)) {
invalidType = null;
} else if("ms.lang.ClassType".equals(fqcn)) {
invalidType = new Mixed[]{this};
} else {
invalidType = new Mixed[types.size()];
for(int i = 0; i < invalidType.length; i++) {
// TODO: For now, we must use this mechanism, since we don't populate the ODT with
// all the native classes. But once we do, we should remove this check entirely here.
if(NativeTypeList.getNativeTypeList().contains(this.fqcn)) {
invalidType[i] = NativeTypeList.getInvalidInstanceForUse(this.fqcn);
} else {
ObjectDefinitionTable odt = env.getEnv(CompilerEnvironment.class).getObjectDefinitionTable();
ObjectDefinition od = odt.get(this.fqcn);
invalidType[i] = new UserObject(Target.UNKNOWN, null, env, od, null);
synchronized(this) {
if(this.invalidType != UNINITIALIZED) {
return;
}
@SuppressWarnings("LocalVariableHidesMemberVariable")
String fqcn = this.fqcn.getFQCN();
try {
Mixed[] invalidType;
if("auto".equals(fqcn)) {
invalidType = null;
} else if("ms.lang.ClassType".equals(fqcn)) {
invalidType = new Mixed[]{this};
} else {
invalidType = new Mixed[this.types.size()];
for(int i = 0; i < invalidType.length; i++) {
// TODO: For now, we must use this mechanism, since we don't populate the ODT with
// all the native classes. But once we do, we should remove this check entirely here.
if(NativeTypeList.getNativeTypeList().contains(this.fqcn)) {
invalidType[i] = NativeTypeList.getInvalidInstanceForUse(this.fqcn);
} else {
ObjectDefinitionTable odt = env.getEnv(CompilerEnvironment.class).getObjectDefinitionTable();
ObjectDefinition od = odt.get(this.fqcn);
invalidType[i] = new UserObject(Target.UNKNOWN, null, env, od, null);
}
}
}
this.invalidType = invalidType;
} catch (ClassNotFoundException | ObjectDefinitionNotFoundException ex) {
throw new Error(ex);
}
} catch (ClassNotFoundException | ObjectDefinitionNotFoundException ex) {
throw new Error(ex);
}
}

@@ -435,14 +442,8 @@ public CClassType[] getInterfaces() {
*/
public CClassType[] getTypeSuperclasses(Environment env) {
instantiateInvalidType(env);
try {
return Stream.of(invalidType).flatMap(e -> Stream.of(e.getSuperclasses()))
.collect(Collectors.toSet()).toArray(CClassType.EMPTY_CLASS_ARRAY);
} catch (NullPointerException ex) {
// There is apparently some case where this can throw an NPE. It's completely unclear
// why or how this happens, so just catch it, log details about this class, and rethrow.
throw new RuntimeException("NPE while calling getSuperclassesForType for type " + getFQCN(), ex);
}
return Stream.of(invalidType).flatMap(e -> Stream.of(e.getSuperclasses()))
.collect(Collectors.toSet()).toArray(CClassType.EMPTY_CLASS_ARRAY);
}

/**
@@ -27,6 +27,7 @@
import com.laytonsmith.core.environments.CommandHelperEnvironment;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.environments.RuntimeMode;
import com.laytonsmith.core.events.Event;
import com.laytonsmith.core.events.EventList;
import com.laytonsmith.core.exceptions.ConfigCompileException;
@@ -54,6 +55,7 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -643,7 +645,10 @@ public void doCompilation(CompletableFuture<ParseTree> future, Executor threadPo
}
Environment env;
try {
env = Static.GenerateStandaloneEnvironment(false);
// Cmdline mode disables things like security checks and whatnot.
// These may be present in the runtime environment,
// but it's not possible for us to tell that at this point.
env = Static.GenerateStandaloneEnvironment(false, EnumSet.of(RuntimeMode.CMDLINE));
// Make this configurable at some point. For now, however, we need this so we can get
// correct handling on minecraft functions.
env = env.cloneAndAdd(new CommandHelperEnvironment());
@@ -654,10 +659,6 @@ public void doCompilation(CompletableFuture<ParseTree> future, Executor threadPo
compilerEnv.setLogCompilerWarnings(false); // No one would see them
GlobalEnv gEnv = env.getEnv(GlobalEnv.class);

// This disables things like security checks and whatnot.
// These may be present in the runtime environment,
// but it's not possible for us to tell that at this point.
gEnv.SetCustom("cmdline", true);
URI uuri = new URI(uri);
File f;
if("untitled".equals(uuri.getScheme())) {

0 comments on commit 067118a

Please sign in to comment.