Adds optional support of throwing NPE when calling method on null

instance
This commit is contained in:
konsoletyper 2014-02-27 10:04:51 +04:00
parent 43acca8706
commit b4347b4eb8
7 changed files with 204 additions and 7 deletions

View File

@ -138,11 +138,10 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
writer.append("var cls = " + self + ".$data;").softNewLine();
writer.append("var ctor = cls.$$constructor$$;").softNewLine();
writer.append("if (!ctor) {").indent().softNewLine();
/*writer.append("var ex = new ").appendClass(InstantiationException.class.getName()).append("();").softNewLine();
writer.append("var ex = new ").appendClass(InstantiationException.class.getName()).append("();").softNewLine();
writer.appendMethodBody(new MethodReference(InstantiationException.class.getName(), new MethodDescriptor(
"<init>", ValueType.VOID))).append("(ex);").softNewLine();*/
//writer.append("$rt_throw(ex);").softNewLine();
writer.append("return null;").softNewLine();
"<init>", ValueType.VOID))).append("(ex);").softNewLine();
writer.append("$rt_throw(ex);").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("var instance = new cls();").softNewLine();
writer.append("ctor(instance);").softNewLine();
@ -180,6 +179,10 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
case "getDeclaringClass":
graph.getResult().propagate("java.lang.Class");
break;
case "newInstance":
checker.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>",
ValueType.VOID), graph.getStack()).use();
break;
}
}
}

View File

@ -133,6 +133,7 @@ public class TClass<T> extends TObject {
}
@GeneratedBy(ClassNativeGenerator.class)
@PluggableDependency(ClassNativeGenerator.class)
public native T newInstance() throws TInstantiationException, TIllegalAccessException;
@GeneratedBy(ClassNativeGenerator.class)

View File

@ -0,0 +1,58 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.javascript;
import org.teavm.model.*;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
/**
*
* @author Alexey Andreev
*/
public class NullPointerExceptionTransformer implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource) {
for (MethodHolder method : cls.getMethods()) {
Program program = method.getProgram();
if (program == null) {
continue;
}
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
transformBlock(block);
}
}
}
private void transformBlock(BasicBlock block) {
for (int i = 0; i < block.getInstructions().size(); ++i) {
Instruction insn = block.getInstructions().get(i);
if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction)insn;
if (invoke.getType() != InvocationType.VIRTUAL) {
continue;
}
InvokeInstruction checkInvoke = new InvokeInstruction();
checkInvoke.setMethod(new MethodReference(RuntimeSupport.class.getName(), "requireNonNull",
ValueType.object("java.lang.Object"), ValueType.VOID));
checkInvoke.setType(InvocationType.SPECIAL);
checkInvoke.getArguments().add(invoke.getInstance());
block.getInstructions().add(i++, checkInvoke);
}
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.javascript;
/**
*
* @author Alexey Andreev
*/
public final class RuntimeSupport {
private RuntimeSupport() {
}
public static void requireNonNull(Object obj) {
if (obj == null) {
throw new NullPointerException();
}
}
}

View File

@ -90,9 +90,9 @@
<scanDependencies>true</scanDependencies>
<outputDir>${project.build.directory}/javascript-tck</outputDir>
<adapterClass>org.teavm.html4j.testing.KOTestAdapter</adapterClass>
<!-- <wildcards>
<param>net.java.html.js.tests.*Test</param>
</wildcards> -->
<transformers>
<param>org.teavm.javascript.NullPointerExceptionTransformer</param>
</transformers>
</configuration>
</execution>
</executions>

View File

@ -16,6 +16,8 @@
package org.teavm.maven;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@ -33,6 +35,7 @@ import org.apache.maven.project.MavenProject;
import org.teavm.common.ThreadPoolFiniteExecutor;
import org.teavm.javascript.JavascriptBuilder;
import org.teavm.javascript.JavascriptBuilderFactory;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
@ -77,6 +80,9 @@ public class BuildJavascriptMojo extends AbstractMojo {
@Parameter(required = false)
private int numThreads = 1;
@Parameter
private String[] transformers;
public void setProject(MavenProject project) {
this.project = project;
}
@ -109,6 +115,14 @@ public class BuildJavascriptMojo extends AbstractMojo {
this.numThreads = numThreads;
}
public String[] getTransformers() {
return transformers;
}
public void setTransformers(String[] transformers) {
this.transformers = transformers;
}
@Override
public void execute() throws MojoExecutionException {
Log log = getLog();
@ -133,6 +147,9 @@ public class BuildJavascriptMojo extends AbstractMojo {
builder.setMinifying(minifying);
builder.setBytecodeLogging(bytecodeLogging);
builder.installPlugins();
for (ClassHolderTransformer transformer : instantiateTransformers(classLoader)) {
builder.add(transformer);
}
builder.prepare();
MethodDescriptor mainMethodDesc = new MethodDescriptor("main", ValueType.arrayOf(
ValueType.object("java.lang.String")), ValueType.VOID);
@ -167,6 +184,41 @@ public class BuildJavascriptMojo extends AbstractMojo {
}
}
private List<ClassHolderTransformer> instantiateTransformers(ClassLoader classLoader)
throws MojoExecutionException {
List<ClassHolderTransformer> transformerInstances = new ArrayList<>();
if (transformers == null) {
return transformerInstances;
}
for (String transformerName : transformers) {
Class<?> transformerRawType;
try {
transformerRawType = Class.forName(transformerName, true, classLoader);
} catch (ClassNotFoundException e) {
throw new MojoExecutionException("Transformer not found: " + transformerName, e);
}
if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) {
throw new MojoExecutionException("Transformer " + transformerName + " is not subtype of " +
ClassHolderTransformer.class.getName());
}
Class<? extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
Constructor<? extends ClassHolderTransformer> ctor;
try {
ctor = transformerType.getConstructor();
} catch (NoSuchMethodException e) {
throw new MojoExecutionException("Transformer " + transformerName + " has no default constructor");
}
try {
ClassHolderTransformer transformer = ctor.newInstance();
transformerInstances.add(transformer);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new MojoExecutionException("Error instantiating transformer " + transformerName, e);
}
}
return transformerInstances;
}
private ClassLoader prepareClassLoader() throws MojoExecutionException {
try {
Log log = getLog();

View File

@ -90,6 +90,11 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
@Parameter
private String adapterClass = JUnitTestAdapter.class.getName();
@Parameter
private String[] transformers;
private List<ClassHolderTransformer> transformerInstances;
public void setProject(MavenProject project) {
this.project = project;
}
@ -122,6 +127,14 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
this.wildcards = wildcards;
}
public String[] getTransformers() {
return transformers;
}
public void setTransformers(String[] transformers) {
this.transformers = transformers;
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
Runnable finalizer = null;
@ -148,6 +161,7 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
findTests(classHolder);
}
transformerInstances = instantiateTransformers(classLoader);
File allTestsFile = new File(outputDir, "tests/all.js");
try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) {
allTestsWriter.write("doRunTests = function() {\n");
@ -291,6 +305,9 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
JavascriptBuilder builder = builderFactory.create();
builder.setMinifying(minifying);
builder.installPlugins();
for (ClassHolderTransformer transformer : transformerInstances) {
builder.add(transformer);
}
builder.prepare();
File file = new File(outputDir, targetName);
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
@ -453,4 +470,39 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
}
}
}
private List<ClassHolderTransformer> instantiateTransformers(ClassLoader classLoader)
throws MojoExecutionException {
List<ClassHolderTransformer> transformerInstances = new ArrayList<>();
if (transformers == null) {
return transformerInstances;
}
for (String transformerName : transformers) {
Class<?> transformerRawType;
try {
transformerRawType = Class.forName(transformerName, true, classLoader);
} catch (ClassNotFoundException e) {
throw new MojoExecutionException("Transformer not found: " + transformerName, e);
}
if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) {
throw new MojoExecutionException("Transformer " + transformerName + " is not subtype of " +
ClassHolderTransformer.class.getName());
}
Class<? extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
Constructor<? extends ClassHolderTransformer> ctor;
try {
ctor = transformerType.getConstructor();
} catch (NoSuchMethodException e) {
throw new MojoExecutionException("Transformer " + transformerName + " has no default constructor");
}
try {
ClassHolderTransformer transformer = ctor.newInstance();
transformerInstances.add(transformer);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new MojoExecutionException("Error instantiating transformer " + transformerName, e);
}
}
return transformerInstances;
}
}