Class contructor and methods shouldn't have "arguments" and "caller"
authorgskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 2 May 2016 12:01:31 +0000 (12:01 +0000)
committergskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 2 May 2016 12:01:31 +0000 (12:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=144238

Reviewed by Ryosuke Niwa.

Source/JavaScriptCore:

Added TypeError that is raised in case of access to properties 'arguments' or 'caller'
of constructor or method of class.

* runtime/JSFunction.cpp:
(JSC::getThrowTypeErrorGetterSetter):
(JSC::JSFunction::getOwnPropertySlot):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::createThrowTypeErrorArgumentsAndCaller):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::throwTypeErrorArgumentsAndCallerGetterSetter):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncThrowTypeErrorArgumentsAndCaller):
* runtime/JSGlobalObjectFunctions.h:

LayoutTests:

* js/class-method-and-constructor-properties-expected.txt: Added.
* js/class-method-and-constructor-properties.html: Added.
* js/script-tests/class-method-and-constructor-properties.js: Added.

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

LayoutTests/ChangeLog
LayoutTests/js/class-method-and-constructor-properties-expected.txt [new file with mode: 0644]
LayoutTests/js/class-method-and-constructor-properties.html [new file with mode: 0644]
LayoutTests/js/script-tests/class-method-and-constructor-properties.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h

index cfc7bf1..321c2c4 100644 (file)
@@ -1,3 +1,14 @@
+2016-05-01  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        Class contructor and methods shouldn't have "arguments" and "caller"
+        https://bugs.webkit.org/show_bug.cgi?id=144238
+
+        Reviewed by Ryosuke Niwa.
+
+        * js/class-method-and-constructor-properties-expected.txt: Added.
+        * js/class-method-and-constructor-properties.html: Added.
+        * js/script-tests/class-method-and-constructor-properties.js: Added.
+
 2016-05-02  Yoav Weiss  <yoav@yoav.ws>
 
         Move ResourceTiming behind a runtime flag
diff --git a/LayoutTests/js/class-method-and-constructor-properties-expected.txt b/LayoutTests/js/class-method-and-constructor-properties-expected.txt
new file mode 100644 (file)
index 0000000..1243f78
--- /dev/null
@@ -0,0 +1,31 @@
+Tests for ES6 class method and constructor properties
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS (new A()).constructor.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new A()).constructor.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new B()).constructor.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new B()).constructor.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new C()).constructor.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new C()).constructor.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new D()).constructor.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new D()).constructor.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new E()).getItem.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new E()).getItem.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new F()).getItem.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new F()).getItem.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new F()).getElement.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new F()).getElement.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new G()).item.caller:::undefined
+PASS (new G()).item.arguments:::undefined
+PASS H.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS H.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new H()).caller():::"value"
+PASS (new H()).arguments():::"value"
+PASS (new H()).caller.caller:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS (new H()).caller.arguments:::TypeError: 'caller' and 'arguments' cannot be accessed in class context.
+PASS successfullyParsed:::true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/class-method-and-constructor-properties.html b/LayoutTests/js/class-method-and-constructor-properties.html
new file mode 100644 (file)
index 0000000..78d469f
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/js-test-pre.js"></script>
+<script src="script-tests/class-method-and-constructor-properties.js"></script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/script-tests/class-method-and-constructor-properties.js b/LayoutTests/js/script-tests/class-method-and-constructor-properties.js
new file mode 100644 (file)
index 0000000..cb84986
--- /dev/null
@@ -0,0 +1,69 @@
+
+description('Tests for ES6 class method and constructor properties');
+
+function shouldThrow(s, message) {
+    var threw = false;
+    try {
+        eval(s);
+    } catch(e) {
+        threw = true;
+        if (!message || e.toString() === eval(message))
+            testPassed(s + ":::" + e.toString());
+        else
+            testFailed(s);
+    }
+    if (!threw)
+        testFailed(s);
+}
+
+function shouldBe(a, b) {
+    var r1 = eval(a);
+    var r2 = eval(b)
+    if (r1 === r2)
+        testPassed(a + ":::" + b);
+    else
+        testFailed(r1 + ":::" + r2);
+}
+
+class A { };
+class B extends A { };
+class C extends A { constructor () {super();}}
+class D { constructor () {}}
+class E { getItem() { return 'text';} }
+class F extends E { getElement() { return 'text'}}
+class G extends E { get item() { return 'text'} }
+
+shouldThrow('(new A()).constructor.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new A()).constructor.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new B()).constructor.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new B()).constructor.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new C()).constructor.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new C()).constructor.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new D()).constructor.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new D()).constructor.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new E()).getItem.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new E()).getItem.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new F()).getItem.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new F()).getItem.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new F()).getElement.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new F()).getElement.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+
+shouldBe('(new G()).item.caller', 'undefined');
+shouldBe('(new G()).item.arguments', 'undefined');
+
+class H {
+  caller() { return 'value'; }
+  arguments() { return 'value'; }
+}
+
+shouldThrow('H.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('H.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+
+shouldBe('(new H()).caller()', '"value"');
+shouldBe('(new H()).arguments()', '"value"');
+
+shouldThrow('(new H()).caller.caller', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+shouldThrow('(new H()).caller.arguments', '"TypeError: \'caller\' and \'arguments\' cannot be accessed in class context."');
+
+
+var successfullyParsed = true;
index a14e1c3..b589668 100644 (file)
@@ -1,3 +1,29 @@
+2016-05-01  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        Class contructor and methods shouldn't have "arguments" and "caller"
+        https://bugs.webkit.org/show_bug.cgi?id=144238
+
+        Reviewed by Ryosuke Niwa.
+
+        Added TypeError that is raised in case of access to properties 'arguments' or 'caller'
+        of constructor or method of class. Actually TypeError already raised for most cases, except
+        case with undeclared constructor e. g. 
+        class A {}
+        (new A).constructor.caller 
+        (new A).constructor.arguments
+
+        * runtime/JSFunction.cpp:
+        (JSC::getThrowTypeErrorGetterSetter):
+        (JSC::JSFunction::getOwnPropertySlot):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::createThrowTypeErrorArgumentsAndCaller):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::throwTypeErrorArgumentsAndCallerGetterSetter):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::globalFuncThrowTypeErrorArgumentsAndCaller):
+        * runtime/JSGlobalObjectFunctions.h:
+
 2016-05-02  Yoav Weiss  <yoav@yoav.ws>
 
         Move ResourceTiming behind a runtime flag
index 2c7c2e8..47ddfbd 100644 (file)
@@ -319,6 +319,13 @@ static JSValue retrieveCallerFunction(ExecState* exec, JSFunction* functionObj)
     return functor.result();
 }
 
+static GetterSetter* getThrowTypeErrorGetterSetter(ExecState* exec, JSFunction* function)
+{
+    return function->jsExecutable()->isClassConstructorFunction() || function->jsExecutable()->parseMode() == SourceParseMode::MethodMode
+        ? function->globalObject()->throwTypeErrorArgumentsAndCallerGetterSetter(exec->vm())
+        : function->globalObject()->throwTypeErrorGetterSetter(exec->vm());
+}
+
 EncodedJSValue JSFunction::callerGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName)
 {
     JSFunction* thisObj = jsCast<JSFunction*>(JSValue::decode(thisValue));
@@ -365,10 +372,11 @@ bool JSFunction::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyN
     }
 
     if (propertyName == exec->propertyNames().arguments) {
-        if (thisObject->jsExecutable()->isStrictMode()) {
+        if (thisObject->jsExecutable()->isStrictMode() || thisObject->jsExecutable()->isClassConstructorFunction()) {
             bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
             if (!result) {
-                thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor);
+                GetterSetter* errorGetterSetter = getThrowTypeErrorGetterSetter(exec, thisObject);
+                thisObject->putDirectAccessor(exec, propertyName, errorGetterSetter, DontDelete | DontEnum | Accessor);
                 result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
                 ASSERT(result);
             }
@@ -379,10 +387,11 @@ bool JSFunction::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyN
     }
 
     if (propertyName == exec->propertyNames().caller) {
-        if (thisObject->jsExecutable()->isStrictMode()) {
+        if (thisObject->jsExecutable()->isStrictMode() || thisObject->jsExecutable()->isClassConstructorFunction()) {
             bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
             if (!result) {
-                thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor);
+                GetterSetter* errorGetterSetter = getThrowTypeErrorGetterSetter(exec, thisObject);
+                thisObject->putDirectAccessor(exec, propertyName, errorGetterSetter, DontDelete | DontEnum | Accessor);
                 result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
                 ASSERT(result);
             }
index 171b0c7..7de9260 100644 (file)
@@ -893,6 +893,15 @@ void JSGlobalObject::createThrowTypeError(VM& vm)
     m_throwTypeErrorGetterSetter.set(vm, this, getterSetter);
 }
 
+void JSGlobalObject::createThrowTypeErrorArgumentsAndCaller(VM& vm)
+{
+    JSFunction* thrower = JSFunction::create(vm, this, 0, String(), globalFuncThrowTypeErrorArgumentsAndCaller);
+    GetterSetter* getterSetter = GetterSetter::create(vm, this);
+    getterSetter->setGetter(vm, this, thrower);
+    getterSetter->setSetter(vm, this, thrower);
+    m_throwTypeErrorArgumentsAndCallerGetterSetter.set(vm, this, getterSetter);
+}
+
 // Set prototype, and also insert the object prototype at the end of the chain.
 void JSGlobalObject::resetPrototype(VM& vm, JSValue prototype)
 {
@@ -942,6 +951,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(&thisObject->m_newPromiseCapabilityFunction);
     visitor.append(&thisObject->m_functionProtoHasInstanceSymbolFunction);
     visitor.append(&thisObject->m_throwTypeErrorGetterSetter);
+    visitor.append(&thisObject->m_throwTypeErrorArgumentsAndCallerGetterSetter);
     visitor.append(&thisObject->m_moduleLoader);
 
     visitor.append(&thisObject->m_objectPrototype);
index d8c58d5..d84890a 100644 (file)
@@ -234,6 +234,7 @@ protected:
     WriteBarrier<JSObject> m_regExpProtoGlobalGetter;
     WriteBarrier<JSObject> m_regExpProtoUnicodeGetter;
     WriteBarrier<GetterSetter> m_throwTypeErrorGetterSetter;
+    WriteBarrier<GetterSetter> m_throwTypeErrorArgumentsAndCallerGetterSetter;
 
     WriteBarrier<ModuleLoaderObject> m_moduleLoader;
 
@@ -468,6 +469,13 @@ public:
         return m_throwTypeErrorGetterSetter.get();
     }
 
+    GetterSetter* throwTypeErrorArgumentsAndCallerGetterSetter(VM& vm)
+    {
+        if (!m_throwTypeErrorArgumentsAndCallerGetterSetter)
+            createThrowTypeErrorArgumentsAndCaller(vm);
+        return m_throwTypeErrorArgumentsAndCallerGetterSetter.get();
+    }
+    
     ModuleLoaderObject* moduleLoader() const { return m_moduleLoader.get(); }
 
     ObjectPrototype* objectPrototype() const { return m_objectPrototype.get(); }
@@ -733,6 +741,7 @@ private:
     JS_EXPORT_PRIVATE void init(VM&);
 
     void createThrowTypeError(VM&);
+    void createThrowTypeErrorArgumentsAndCaller(VM&);
 
     JS_EXPORT_PRIVATE static void clearRareData(JSCell*);
 };
index 3813faf..6bbaac9 100644 (file)
@@ -781,6 +781,11 @@ EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState* exec)
 {
     return throwVMTypeError(exec);
 }
+    
+EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeErrorArgumentsAndCaller(ExecState* exec)
+{
+    return throwVMTypeError(exec, "'caller' and 'arguments' cannot be accessed in class context.");
+}
 
 class GlobalFuncProtoGetterFunctor {
 public:
index b929f51..52c507f 100644 (file)
@@ -49,6 +49,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState*);
+EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeErrorArgumentsAndCaller(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState*);