diff --git a/src/com/google/javascript/jscomp/RuntimeTypeCheck.java b/src/com/google/javascript/jscomp/RuntimeTypeCheck.java index 73454daca7a..8436bbc9b8b 100644 --- a/src/com/google/javascript/jscomp/RuntimeTypeCheck.java +++ b/src/com/google/javascript/jscomp/RuntimeTypeCheck.java @@ -16,6 +16,9 @@ package com.google.javascript.jscomp; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.javascript.rhino.IR; @@ -371,19 +374,27 @@ private Node createCheckerNode(JSType type) { private void addBoilerplateCode() { Node newNode = compiler.ensureLibraryInjected("runtime_type_check", false); - if (newNode != null && logFunction != null) { - // Inject the custom log function. - Node logOverride = IR.exprResult( - IR.assign( - NodeUtil.newQName( - compiler, - "$jscomp.typecheck.log"), - NodeUtil.newQName( - compiler, - logFunction))); - newNode.getParent().addChildAfter(logOverride, newNode); - compiler.reportChangeToEnclosingScope(newNode); + if (newNode != null) { + injectCustomLogFunction(newNode); + } + } + + @VisibleForTesting + void injectCustomLogFunction(Node node) { + if (logFunction == null) { + return; } + checkState( + NodeUtil.isValidQualifiedName(compiler.getFeatureSet(), logFunction), + "%s is not a valid qualified name", logFunction); + Node logOverride = + IR.exprResult( + IR.assign( + NodeUtil.newQName(compiler, "$jscomp.typecheck.log"), + NodeUtil.newQName(compiler, logFunction))); + checkState(node.getParent().isScript(), node.getParent()); + node.getParent().addChildAfter(logOverride, node); + compiler.reportChangeToEnclosingScope(node); } private Node jsCode(String prop) { diff --git a/test/com/google/javascript/jscomp/RuntimeTypeCheckTest.java b/test/com/google/javascript/jscomp/RuntimeTypeCheckTest.java index e5713de3774..c061b5a60b7 100644 --- a/test/com/google/javascript/jscomp/RuntimeTypeCheckTest.java +++ b/test/com/google/javascript/jscomp/RuntimeTypeCheckTest.java @@ -18,11 +18,16 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import javax.annotation.Nullable; + /** * Tests for {@link RuntimeTypeCheck}. * */ public final class RuntimeTypeCheckTest extends CompilerTestCase { + @Nullable private String logFunction = null; public RuntimeTypeCheckTest() { super("/** @const */ var undefined;"); @@ -313,6 +318,40 @@ public void testFunctionType() { testChecksSame("/** @type {!Function} */function f() {}"); } + public void testInjectLogFunction_name() { + logFunction = "myLogFn"; + Compiler compiler = createCompiler(); + compiler.initOptions(getOptions()); + Node testNode = IR.exprResult(IR.nullNode()); + IR.script(testNode); + getProcessor(compiler).injectCustomLogFunction(testNode); + assertThat(compiler.toSource(testNode.getParent())).contains("$jscomp.typecheck.log=myLogFn"); + } + + public void testInjectLogFunction_qualifiedName() { + logFunction = "my.log.fn"; + Compiler compiler = createCompiler(); + compiler.initOptions(getOptions()); + Node testNode = IR.exprResult(IR.nullNode()); + IR.script(testNode); + getProcessor(compiler).injectCustomLogFunction(testNode); + assertThat(compiler.toSource(testNode.getParent())).contains("$jscomp.typecheck.log=my.log.fn"); + } + + public void testInvalidLogFunction() { + logFunction = "{}"; // Not a valid qualified name + Compiler compiler = createCompiler(); + compiler.initOptions(getOptions()); + Node testNode = IR.exprResult(IR.nullNode()); + IR.script(testNode); + try { + getProcessor(compiler).injectCustomLogFunction(testNode); + fail("Expected an IllegalStateException"); + } catch (IllegalStateException e) { + assertThat(e).hasMessageThat().contains("not a valid qualified name"); + } + } + private void testChecks(String js, String expected) { test(js, expected); assertThat(getLastCompiler().injected).containsExactly("runtime_type_check"); @@ -334,8 +373,8 @@ protected NoninjectingCompiler getLastCompiler() { } @Override - protected CompilerPass getProcessor(final Compiler compiler) { - return new RuntimeTypeCheck(compiler, null); + protected RuntimeTypeCheck getProcessor(final Compiler compiler) { + return new RuntimeTypeCheck(compiler, logFunction); } @Override