Skip to content

Commit

Permalink
Prevent functions from being inlined when they contain a reference to…
Browse files Browse the repository at this point in the history
… "super".

Related to: #3015
Related to: #3046

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=207971077
  • Loading branch information
MatthewMerrill authored and tjgq committed Aug 10, 2018
1 parent ed42f9d commit a3c8b44
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 16 deletions.
34 changes: 18 additions & 16 deletions src/com/google/javascript/jscomp/FunctionInjector.java
Expand Up @@ -159,22 +159,24 @@ boolean doesFunctionMeetMinimumRequirements(final String fnName, Node fnNode) {
boolean referencesArguments = NodeUtil.isNameReferenced( boolean referencesArguments = NodeUtil.isNameReferenced(
block, "arguments", NodeUtil.MATCH_NOT_FUNCTION); block, "arguments", NodeUtil.MATCH_NOT_FUNCTION);


// or it references "eval" or one of its names anywhere. Predicate<Node> blocksInjection =
Predicate<Node> p = new Predicate<Node>(){ new Predicate<Node>() {
@Override @Override
public boolean apply(Node n) { public boolean apply(Node n) {
if (n.isName()) { if (n.isName()) {
return n.getString().equals("eval") // References "eval" or one of its names anywhere.
|| (!fnName.isEmpty() return n.getString().equals("eval")
&& n.getString().equals(fnName)) || (!fnName.isEmpty() && n.getString().equals(fnName))
|| (!fnRecursionName.isEmpty() || (!fnRecursionName.isEmpty() && n.getString().equals(fnRecursionName));
&& n.getString().equals(fnRecursionName)); } else if (n.isSuper()) {
} // Don't inline if this function or its inner functions contains super
return false; return true;
} }
}; return false;
}
};


return !referencesArguments && !NodeUtil.has(block, p, Predicates.alwaysTrue()); return !referencesArguments && !NodeUtil.has(block, blocksInjection, Predicates.alwaysTrue());
} }


/** /**
Expand Down Expand Up @@ -372,7 +374,7 @@ public void prepare(FunctionInjector injector, Reference ref) {
/** /**
* An var declaration and initialization, where the result of the call is * An var declaration and initialization, where the result of the call is
* assigned to the declared name * assigned to the declared name
* name. For example: "a = foo();". * name. For example: "var a = foo();".
* VAR * VAR
* NAME A * NAME A
* CALL * CALL
Expand Down
18 changes: 18 additions & 0 deletions test/com/google/javascript/jscomp/InlineFunctionsTest.java
Expand Up @@ -3382,4 +3382,22 @@ public void testFunctionReferencingLetInNonGlobalBlock() {
"}", "}",
"alert(g(10));")); "alert(g(10));"));
} }

public void testNotInliningFunctionWithSuper() {
// Super field accessor arrow functions like this one are used for transpilation of some
// features such as async functions and async generators. If inlined, it becomes a syntax error
// as the inner function has no super.
testSame(
lines(
"class A { m(){ return 1 } };",
"class B extends A {",
" m() {",
" const super$m = () => super.m;",
" const jscomp$this = this;",
" return function*() {",
" yield super$m().call(jscomp$this);",
" };",
" }",
"}"));
}
} }
103 changes: 103 additions & 0 deletions test/com/google/javascript/jscomp/IntegrationTest.java
Expand Up @@ -3275,6 +3275,109 @@ public void testAsyncFunctionInExterns() {
TypeCheck.INEXISTENT_PROPERTY); TypeCheck.INEXISTENT_PROPERTY);
} }


public void testAsyncFunctionSuper() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setLanguageIn(LanguageMode.ECMASCRIPT_2017);
options.setLanguageOut(LanguageMode.ECMASCRIPT_2015);
options.setPropertyRenaming(PropertyRenamingPolicy.OFF);
options.setVariableRenaming(VariableRenamingPolicy.OFF);
options.setPrettyPrint(true);

// Create a noninjecting compiler avoid comparing all the polyfill code.
useNoninjectingCompiler = true;

// include externs definitions for the stuff that would have been injected
ImmutableList.Builder<SourceFile> externsList = ImmutableList.builder();
externsList.addAll(externs);
externsList.add(SourceFile.fromCode("extraExterns", "var $jscomp = {};"));
externs = externsList.build();

test(
options,
lines(
"class Foo {",
" async bar() {",
" console.log('bar');",
" }",
"}",
"",
"class Baz extends Foo {",
" async bar() {",
" await Promise.resolve();",
" super.bar();",
" }",
"}\n"),
lines(
"class Foo {",
" bar() {",
" return $jscomp.asyncExecutePromiseGeneratorFunction(function*() {",
" console.log(\"bar\");",
" });",
" }",
"}",
"class Baz extends Foo {",
" bar() {",
" const $jscomp$async$this = this, $jscomp$async$super$get$bar = () => super.bar;",
" return $jscomp.asyncExecutePromiseGeneratorFunction(function*() {",
" yield Promise.resolve();",
" $jscomp$async$super$get$bar().call($jscomp$async$this);",
" });",
" }",
"}"));
}

public void testAsyncIterationSuper() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setLanguageIn(LanguageMode.ECMASCRIPT_NEXT);
options.setLanguageOut(LanguageMode.ECMASCRIPT_2017);
options.setPropertyRenaming(PropertyRenamingPolicy.OFF);
options.setVariableRenaming(VariableRenamingPolicy.OFF);
options.setPrettyPrint(true);

// Create a noninjecting compiler avoid comparing all the polyfill code.
useNoninjectingCompiler = true;

// include externs definitions for the stuff that would have been injected
ImmutableList.Builder<SourceFile> externsList = ImmutableList.builder();
externsList.addAll(externs);
externsList.add(SourceFile.fromCode("extraExterns", "var $jscomp = {};"));
externs = externsList.build();

test(
options,
lines(
"class Foo {",
" async *bar() {",
" console.log('bar');",
" }",
"}",
"",
"class Baz extends Foo {",
" async *bar() {",
" super.bar().next();",
" }",
"}\n"),
lines(
"class Foo {",
" bar() {",
" return new $jscomp.AsyncGeneratorWrapper(function*() {",
" console.log(\"bar\");",
" }());",
" }",
"}",
"class Baz extends Foo {",
" bar() {",
" const $jscomp$asyncIter$this = this,",
" $jscomp$asyncIter$super$get$bar = () => super.bar;",
" return new $jscomp.AsyncGeneratorWrapper(function*() {",
" $jscomp$asyncIter$super$get$bar().call($jscomp$asyncIter$this).next();",
" }());",
" }",
"}"));
}

public void testLanguageMode() { public void testLanguageMode() {
CompilerOptions options = createCompilerOptions(); CompilerOptions options = createCompilerOptions();


Expand Down

0 comments on commit a3c8b44

Please sign in to comment.