Bug 646597 - Make functions made by the Function constructor compile-and-go. Most of patch was originally written by jorendorff. (r=luke)
authorShu-yu Guo <shu@rfrn.org>
Sat, 04 May 2013 20:53:21 -0700
changeset 141818 ed26fdbe84444121879af267cb0871873ad395ad
parent 141817 1a60a6e9d9b8bd5f6efb50f35410974236a58e7b
child 141819 8eccffccac1cabed4a760a5651265445ef527e1c
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs646597
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 646597 - Make functions made by the Function constructor compile-and-go. Most of patch was originally written by jorendorff. (r=luke)
js/src/frontend/BytecodeCompiler.cpp
js/src/jit-test/tests/auto-regress/bug677977.js
js/src/jit-test/tests/basic/expression-autopsy.js
js/src/jit-test/tests/basic/testLet.js
js/src/jit-test/tests/basic/testScriptCloning.js
js/src/jit-test/tests/debug/onNewScript-01.js
js/src/jit-test/tests/debug/onNewScript-02.js
js/src/jsfun.cpp
js/src/tests/js1_5/Regress/regress-127557.js
js/src/tests/js1_5/extensions/regress-300079.js
js/src/tests/lib/jittests.py
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -325,17 +325,16 @@ frontend::CompileFunctionBody(JSContext 
     ScriptSourceHolder ssh(ss);
     SourceCompressionToken sct(cx);
     JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE);
     if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) {
         if (!ss->setSourceCopy(cx, chars, length, true, &sct))
             return false;
     }
 
-    options.setCompileAndGo(false);
     Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true);
     if (!parser.init())
         return false;
     parser.sct = &sct;
 
     JS_ASSERT(fun);
 
     fun->setArgCount(formals.length());
@@ -408,19 +407,27 @@ frontend::CompileFunctionBody(JSContext 
             funbox->object = moduleFun;
             fun.set(moduleFun); // replace the existing function with the LinkAsmJS native
             generateBytecode = false;
         }
     }
 #endif
 
     if (generateBytecode) {
+        /*
+         * The reason for checking fun->environment() below is that certain
+         * consumers of JS::CompileFunction, namely
+         * nsEventListenerManager::CompileEventHandlerInternal, passes in a
+         * NULL environment. This compiled function is never used, but instead
+         * is cloned immediately onto the right scope chain.
+         */
         BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script,
                                /* evalCaller = */ NullPtr(),
-                               /* hasGlobalScope = */ false, options.lineno);
+                               fun->environment() && fun->environment()->isGlobal(),
+                               options.lineno);
         if (!funbce.init())
             return false;
 
         if (!EmitFunctionScript(cx, &funbce, pn))
             return false;
     }
 
     if (!SetSourceMap(cx, parser.tokenStream, ss, script))
--- a/js/src/jit-test/tests/auto-regress/bug677977.js
+++ b/js/src/jit-test/tests/auto-regress/bug677977.js
@@ -15,12 +15,12 @@ function test(s) {
 }
 test("i = 128;\n" +  "}\n");
 var hits = 0;
 dbg.onNewScript = function (s) {
     hits++;
 };
 assertEq(g.eval("eval('2 + 3')"), 5);
 this.gczeal(hits, 2);
-var fn = g.Function("a", "return 5 + a;");
+var fn = g.evaluate("(function (a) { return 5 + a; })", {compileAndGo: false});
 var g2 = newGlobal('new-compartment');
 dbg.addDebuggee(g2, dbg);
 g2.clone(fn);
--- a/js/src/jit-test/tests/basic/expression-autopsy.js
+++ b/js/src/jit-test/tests/basic/expression-autopsy.js
@@ -1,8 +1,13 @@
+// |jit-test| no-ion
+//
+// We can't guarantee error identity when functions are Ion-compiled due to
+// optimization.
+
 function check_one(expected, f, err) {
     var failed = true;
     try {
         f();
         failed = false;
     } catch (ex) {
         var s = ex.toString();
         assertEq(s.slice(0, 11), "TypeError: ");
--- a/js/src/jit-test/tests/basic/testLet.js
+++ b/js/src/jit-test/tests/basic/testLet.js
@@ -14,19 +14,19 @@ function test(str, arg, result)
         print("EXPECT: " + expect);
         assertEq(got, expect);
     }
 
     // test reflection logic
     Reflect.parse(got);
 
     // test xdr by cloning a cross-compartment function
-    otherGlobal.str = str;
-    var c = clone(otherGlobal.eval("new Function('x', str)"));
-    assertEq(c.toSource(), fun.toSource());
+    var code = "(function (x) { " + str + " })";
+    var c = clone(otherGlobal.evaluate(code, {compileAndGo: false}));
+    assertEq(c.toSource(), eval(code).toSource());
 
     var got = fun(arg);
     var expect = result;
     if (got !== expect) {
         print("GOT:" + got);
         print("EXPECT: " + expect);
         assertEq(got, expect);
     }
--- a/js/src/jit-test/tests/basic/testScriptCloning.js
+++ b/js/src/jit-test/tests/basic/testScriptCloning.js
@@ -1,24 +1,28 @@
 var g = newGlobal('new-compartment');
 
-g.f = new Function('return function(x) { return x }');
+function cloneableFunction(body) {
+    return evaluate("(function () { " + body + " })", {compileAndGo: false});
+}
+
+g.f = cloneableFunction('return function(x) { return x };');
 assertEq(g.eval("clone(f)()(9)"), 9);
 
-g.f = new Function('return function(x) { let(y = x+1) { return y } }');
-assertEq(g.eval("clone(f)()(9)"), 10);
-
-g.f = new Function('return function(x) { let(y = x, z = 1) { return y+z } }');
+g.f = cloneableFunction('return function(x) { let(y = x+1) { return y } };');
 assertEq(g.eval("clone(f)()(9)"), 10);
 
-g.f = new Function('return function(x) { return x.search(/ponies/) }');
+g.f = cloneableFunction('return function(x) { let(y = x, z = 1) { return y+z } };');
+assertEq(g.eval("clone(f)()(9)"), 10);
+
+g.f = cloneableFunction('return function(x) { return x.search(/ponies/) };');
 assertEq(g.eval("clone(f)()('123ponies')"), 3);
 
-g.f = new Function('return function(x,y) { return x.search(/a/) + y.search(/b/) }');
+g.f = cloneableFunction('return function(x,y) { return x.search(/a/) + y.search(/b/) };');
 assertEq(g.eval("clone(f)()('12a','foo')"), 1);
 
-g.f = new Function('return [function(x) x+2, function(y) let(z=y+1) z]');
+g.f = cloneableFunction('return [function(x) x+2, function(y) let(z=y+1) z];');
 assertEq(g.eval("let ([f,g] = clone(f)()) f(g(4))"), 7);
 
-g.f = new Function('return function(x) { switch(x) { case "a": return "b"; case null: return "c" } }');
+g.f = cloneableFunction('return function(x) { switch(x) { case "a": return "b"; case null: return "c" } };');
 assertEq(g.eval("clone(f)()('a')"), "b");
 assertEq(g.eval("clone(f)()(null)"), "c");
 assertEq(g.eval("clone(f)()(3)"), undefined);
--- a/js/src/jit-test/tests/debug/onNewScript-01.js
+++ b/js/src/jit-test/tests/debug/onNewScript-01.js
@@ -32,13 +32,14 @@ assertEq(hits, 1);
 // function code
 hits = 0;
 var fn = g.Function("a", "return 5 + a;");
 assertEq(hits, 1);
 assertEq(fn(8), 13);
 assertEq(hits, 1);
 
 // cloning functions across compartments
+fn = g.evaluate("(function(a) { return 5 + a; })", {compileAndGo: false});
 var g2 = newGlobal('new-compartment');
 dbg.addDebuggee(g2, dbg);
 hits = 0;
 g2.clone(fn);
 assertEq(hits, 1);
--- a/js/src/jit-test/tests/debug/onNewScript-02.js
+++ b/js/src/jit-test/tests/debug/onNewScript-02.js
@@ -39,17 +39,17 @@ test(function () { g.eval("[3].map(funct
 
 // eval with getters and setters
 test(function () { g.eval("var obj = {get x() { return 1; }, set x(v) { print(v); }};"); });
 
 // Function with nested functions
 test(function () { return g.Function("a", "b", "return b - a;"); });
 
 // cloning a function with nested functions
-test(function () { g.clone(Function("x", "return x + 1")); });
+test(function () { g.clone(evaluate("(function(x) { return x + 1; })", {compileAndGo: false})); });
 
 // eval declaring a generator
 test(function () { g.eval("function r(n) { for (var i=0;i<n;i++) yield i; }"); });
 
 // eval with a generator-expression
 test(function () { g.eval("var it = (obj[p] for (p in obj));"); });
 
 // eval creating several instances of a closure
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1291,17 +1291,18 @@ js::Function(JSContext *cx, unsigned arg
     unsigned lineno;
     JSPrincipals *originPrincipals;
     CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals);
     JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
 
     CompileOptions options(cx);
     options.setPrincipals(principals)
            .setOriginPrincipals(originPrincipals)
-           .setFileAndLine(filename, lineno);
+           .setFileAndLine(filename, lineno)
+           .setCompileAndGo(true);
 
     unsigned n = args.length() ? args.length() - 1 : 0;
     if (n > 0) {
         /*
          * Collect the function-argument arguments into one string, separated
          * by commas, then make a tokenstream from that string, and scan it to
          * get the arguments.  We need to throw the full scanner at the
          * problem, because the argument string can legitimately contain
--- a/js/src/tests/js1_5/Regress/regress-127557.js
+++ b/js/src/tests/js1_5/Regress/regress-127557.js
@@ -23,24 +23,25 @@ var summary = 'Testing cloned function o
 var cnCOMMA = ',';
 var status = '';
 var statusitems = [];
 var actual = '';
 var actualvalues = [];
 var expect= '';
 var expectedvalues = [];
 
-var f = Function("x","y","\
-                 function h() { return h_peer(); }               \
-                 function h_peer() { return (x + cnCOMMA + y); } \
-                 return h");
-
 if (typeof clone == 'function')
 {
   status = inSection(1);
+  var f = evaluate("(function(x, y) {\n" +
+                   "    function h() { return h_peer(); }\n" +
+                   "    function h_peer() { return (x + cnCOMMA + y); }\n" +
+                   "    return h;\n" +
+                   "})",
+                   {compileAndGo: false});
   var g = clone(f);
   g.prototype = new Object;
   var h = g(5,6);
   actual = h();
   expect = '5,6';
   addThis();
 }
 else
--- a/js/src/tests/js1_5/extensions/regress-300079.js
+++ b/js/src/tests/js1_5/extensions/regress-300079.js
@@ -22,17 +22,17 @@ function test()
 
   if (typeof clone == 'undefined') {
     expect = 'SKIPPED';
     actual = 'SKIPPED';
   }
   else {
     expect = 'PASSED';
 
-    f = Function("return a * a;");
+    f = evaluate("(function () { return a * a; })", {compileAndGo: false});
     g = clone(f, {a: 3});
     f = null;
     gc();
     try {
       a_squared = g(2);
       if (a_squared != 9)
         throw "Unexpected return from g: a_squared == " + a_squared;
       actual = "PASSED";
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -135,16 +135,18 @@ class Test:
                     elif name == 'debug':
                         test.jitflags.append('--debugjit')
                     elif name == 'mjit':
                         test.jitflags.append('--jm')
                     elif name == 'no-jm':
                         test.jitflags.append('--no-jm')
                     elif name == 'ion-eager':
                         test.jitflags.append('--ion-eager')
+                    elif name == 'no-ion':
+                        test.jitflags.append('--no-ion')
                     elif name == 'dump-bytecode':
                         test.jitflags.append('--dump-bytecode')
                     else:
                         print('warning: unrecognized |jit-test| attribute %s' % part)
 
         if options.valgrind_all:
             test.valgrind = True