Make Builtin functions non constructible
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 19 Apr 2015 18:59:39 +0000 (18:59 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 19 Apr 2015 18:59:39 +0000 (18:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=143923

Reviewed by Darin Adler.

Builtin functions defined by builtins/*.js accidentally have [[Construct]].
According to the spec, these functions except for explicitly defined as a constructor do not have [[Construct]].
This patch fixes it. When the JS function used for a construction is builtin function, throw not a constructor error.

Ideally, returning ConstructTypeNone in JSFunction::getConstructData is enough.
However, to avoid calling getConstructData (it involves indirect call of function pointer of getConstructData), some places do not check ConstructType.
In these places, they only check the target function is JSFunction because previously JSFunction always has [[Construct]].
So in this patch, we check `isBuiltinFunction()` in those places.

* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::inliningCost):
* jit/JITOperations.cpp:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::setUpCall):
* runtime/JSFunction.cpp:
(JSC::JSFunction::getConstructData):
* tests/stress/builtin-function-is-construct-type-none.js: Added.
(shouldThrow):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@182995 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/tests/stress/builtin-function-is-construct-type-none.js [new file with mode: 0644]

index 606fd20..003ea5d 100644 (file)
@@ -1,5 +1,31 @@
 2015-04-19  Yusuke Suzuki  <utatane.tea@gmail.com>
 
+        Make Builtin functions non constructible
+        https://bugs.webkit.org/show_bug.cgi?id=143923
+
+        Reviewed by Darin Adler.
+
+        Builtin functions defined by builtins/*.js accidentally have [[Construct]].
+        According to the spec, these functions except for explicitly defined as a constructor do not have [[Construct]].
+        This patch fixes it. When the JS function used for a construction is builtin function, throw not a constructor error.
+
+        Ideally, returning ConstructTypeNone in JSFunction::getConstructData is enough.
+        However, to avoid calling getConstructData (it involves indirect call of function pointer of getConstructData), some places do not check ConstructType.
+        In these places, they only check the target function is JSFunction because previously JSFunction always has [[Construct]].
+        So in this patch, we check `isBuiltinFunction()` in those places.
+
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::inliningCost):
+        * jit/JITOperations.cpp:
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::setUpCall):
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::getConstructData):
+        * tests/stress/builtin-function-is-construct-type-none.js: Added.
+        (shouldThrow):
+
+2015-04-19  Yusuke Suzuki  <utatane.tea@gmail.com>
+
         [ES6] Implement WeakSet
         https://bugs.webkit.org/show_bug.cgi?id=142408
 
index d973885..970db15 100644 (file)
@@ -1208,11 +1208,12 @@ unsigned ByteCodeParser::inliningCost(CallVariant callee, int argumentCountInclu
     }
     
     // Do we have a code block, and does the code block's size match the heuristics/requirements for
-    // being an inline candidate? We might not have a code block if code was thrown away or if we
-    // simply hadn't actually made this call yet. We could still theoretically attempt to inline it
-    // if we had a static proof of what was being called; this might happen for example if you call a
-    // global function, where watchpointing gives us static information. Overall, it's a rare case
-    // because we expect that any hot callees would have already been compiled.
+    // being an inline candidate? We might not have a code block (1) if code was thrown away,
+    // (2) if we simply hadn't actually made this call yet or (3) code is a builtin function and
+    // specialization kind is construct. In the former 2 cases, we could still theoretically attempt
+    // to inline it if we had a static proof of what was being called; this might happen for example
+    // if you call a global function, where watchpointing gives us static information. Overall,
+    // it's a rare case because we expect that any hot callees would have already been compiled.
     CodeBlock* codeBlock = executable->baselineCodeBlockFor(kind);
     if (!codeBlock) {
         if (verbose)
index be34180..7214973 100644 (file)
@@ -734,6 +734,12 @@ inline char* linkFor(
         codePtr = executable->entrypointFor(*vm, kind, MustCheckArity, registers);
     else {
         FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
+
+        if (!isCall(kind) && functionExecutable->isBuiltinFunction()) {
+            exec->vm().throwException(exec, createNotAConstructorError(exec, callee));
+            return reinterpret_cast<char*>(vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress());
+        }
+
         JSObject* error = functionExecutable->prepareForExecution(execCallee, callee, scope, kind);
         if (error) {
             throwStackOverflowError(exec);
@@ -793,6 +799,12 @@ inline char* virtualForWithFunction(
     ExecutableBase* executable = function->executable();
     if (UNLIKELY(!executable->hasJITCodeFor(kind))) {
         FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
+
+        if (!isCall(kind) && functionExecutable->isBuiltinFunction()) {
+            exec->vm().throwException(exec, createNotAConstructorError(exec, function));
+            return reinterpret_cast<char*>(vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress());
+        }
+
         JSObject* error = functionExecutable->prepareForExecution(execCallee, function, scope, kind);
         if (error) {
             exec->vm().throwException(exec, error);
index 069755b..a029938 100644 (file)
@@ -1097,6 +1097,10 @@ inline SlowPathReturnType setUpCall(ExecState* execCallee, Instruction* pc, Code
         codePtr = executable->entrypointFor(vm, kind, MustCheckArity, RegisterPreservationNotRequired);
     else {
         FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
+
+        if (!isCall(kind) && functionExecutable->isBuiltinFunction())
+            LLINT_CALL_THROW(exec, createNotAConstructorError(exec, callee));
+
         JSObject* error = functionExecutable->prepareForExecution(execCallee, callee, scope, kind);
         if (error)
             LLINT_CALL_THROW(exec, error);
index 6840a40..ed706ea 100644 (file)
@@ -501,6 +501,10 @@ bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, PropertyNa
 ConstructType JSFunction::getConstructData(JSCell* cell, ConstructData& constructData)
 {
     JSFunction* thisObject = jsCast<JSFunction*>(cell);
+
+    if (thisObject->isBuiltinFunction())
+        return ConstructTypeNone;
+
     if (thisObject->isHostFunction()) {
         constructData.native.function = thisObject->nativeConstructor();
         return ConstructTypeHost;
diff --git a/Source/JavaScriptCore/tests/stress/builtin-function-is-construct-type-none.js b/Source/JavaScriptCore/tests/stress/builtin-function-is-construct-type-none.js
new file mode 100644 (file)
index 0000000..626248d
--- /dev/null
@@ -0,0 +1,18 @@
+function shouldThrow(func, message) {
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("not thrown.");
+    if (String(error) !== message)
+        throw new Error("bad error: " + String(error));
+}
+
+for (var i = 0; i < 10000; ++i) {
+    shouldThrow(function () {
+        new Array.prototype.forEach(function () { });
+    }, "TypeError: function is not a constructor (evaluating 'new Array.prototype.forEach(function () { })')");
+}