From ef0e6dfbeff6bdf76e1ed0bb135582cfc07f1199 Mon Sep 17 00:00:00 2001
From: Leo Balter <leonardo.balter@gmail.com>
Date: Fri, 16 Nov 2018 14:03:59 -0500
Subject: [PATCH] new template and cases for private methods

---
 .../private-methods/cls-decl.template         | 123 ++++++++++++++++++
 .../private-methods/cls-expr.template         | 123 ++++++++++++++++++
 .../prod-private-async-generator.case         |  34 +++++
 .../prod-private-async-method.case            |  29 +++++
 .../prod-private-generator.case               |  23 ++++
 src/class-elements/prod-private-method.case   |  18 +++
 6 files changed, 350 insertions(+)
 create mode 100644 src/class-elements/private-methods/cls-decl.template
 create mode 100644 src/class-elements/private-methods/cls-expr.template
 create mode 100644 src/class-elements/prod-private-async-generator.case
 create mode 100644 src/class-elements/prod-private-async-method.case
 create mode 100644 src/class-elements/prod-private-generator.case
 create mode 100644 src/class-elements/prod-private-method.case

diff --git a/src/class-elements/private-methods/cls-decl.template b/src/class-elements/private-methods/cls-decl.template
new file mode 100644
index 0000000000..677c338146
--- /dev/null
+++ b/src/class-elements/private-methods/cls-decl.template
@@ -0,0 +1,123 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+path: language/statements/class/private-methods/
+name: private method definitions in a class declaration
+features: [class, class-methods-private]
+info: |
+  ClassElement :
+    MethodDefinition
+    ...
+    ;
+
+  ClassElementName :
+    PropertyName
+    PrivateName
+
+  PrivateName ::
+    # IdentifierName
+
+  MethodDefinition :
+    ClassElementName ( UniqueFormalParameters ) { FunctionBody }
+    GeneratorMethod
+    AsyncMethod
+    AsyncGeneratorMethod 
+    get ClassElementName () { FunctionBody }
+    set ClassElementName ( PropertySetParameterList ) { FunctionBody }
+  
+  GeneratorMethod :
+    * ClassElementName ( UniqueFormalParameters ){GeneratorBody}
+
+  AsyncMethod :
+    async [no LineTerminator here] ClassElementName ( UniqueFormalParameters ) { AsyncFunctionBody }
+
+  AsyncGeneratorMethod :
+    async [no LineTerminator here]* ClassElementName ( UniqueFormalParameters ) { AsyncGeneratorBody }
+
+  ---
+
+  InitializeClassElements ( F, proto )
+
+  ...
+  5. For each item element in order from elements,
+    a. Assert: If element.[[Placement]] is "prototype" or "static", then element.[[Key]] is not a Private Name.
+    b. If element.[[Kind]] is "method" and element.[[Placement]] is "static" or "prototype",
+      i. Let receiver be F if element.[[Placement]] is "static", else let receiver be proto.
+      ii. Perform ? DefineClassElement(receiver, element).
+
+  InitializeInstanceElements ( O, constructor )
+
+  ...
+  3. Let elements be the value of F's [[Elements]] internal slot.
+  4. For each item element in order from elements,
+    a. If element.[[Placement]] is "own" and element.[[Kind]] is "method",
+      i. Perform ? DefineClassElement(O, element).
+
+  DefineClassElement (receiver, element)
+
+  ...
+  6. If key is a Private Name,
+    a. Perform ? PrivateFieldDefine(receiver, key, descriptor).
+
+  PrivateFieldDefine (P, O, desc)
+
+  ...
+  6. Append { [[PrivateName]]: P, [[PrivateFieldDescriptor]]: desc } to O.[[PrivateFieldDescriptors]].
+
+esid: prod-MethodDefinition
+---*/
+
+/*** template notes
+ * method should always be #m
+ * the template provides c.ref() for external reference
+ */
+
+function hasOwnProperty(obj, name) {
+  return Object.prototype.hasOwnProperty.call(obj, name);
+}
+
+class C {
+  /*{ element }*/
+
+  get ref() { return this.#m; }
+
+  constructor() {
+    assert.sameValue(
+      hasOwnProperty(this, '#m'), false,
+      'private methods are defined in an special internal slot and cannot be found as own properties'
+    );
+    assert.sameValue(typeof this.#m, 'function');
+    assert.sameValue(this.ref(), this.#m, 'returns the same value');
+
+    /*{ constructor }*/
+  }
+}
+
+var c = new C();
+var other = new C();
+
+assert.sameValue(
+  hasOwnProperty(C.prototype, '#m'), false,
+  'method is not defined in the prototype'
+);
+
+assert.sameValue(
+  hasOwnProperty(C, '#m'), false,
+  'method is not defined in the contructor'
+);
+
+assert.sameValue(
+  hasOwnProperty(c, '#m'), false,
+  'method cannot be seen outside of the class'
+);
+
+/***
+ * MethodDefinition : ClassElementName ( UniqueFormalParameters ) { FunctionBody }
+ * 
+ * 1. Let methodDef be DefineMethod of MethodDefinition with argument homeObject.
+ * ...
+ */
+assert.sameValue(c.ref, other.ref, 'The method is defined once, and reused on every new instance');
+
+/*{ assertions }*/
diff --git a/src/class-elements/private-methods/cls-expr.template b/src/class-elements/private-methods/cls-expr.template
new file mode 100644
index 0000000000..781304d72c
--- /dev/null
+++ b/src/class-elements/private-methods/cls-expr.template
@@ -0,0 +1,123 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+path: language/expressions/class/private-methods/
+name: private method definitions in a class expression
+info: |
+  ClassElement :
+    MethodDefinition
+    ...
+    ;
+
+  ClassElementName :
+    PropertyName
+    PrivateName
+
+  PrivateName ::
+    # IdentifierName
+
+  MethodDefinition :
+    ClassElementName ( UniqueFormalParameters ) { FunctionBody }
+    GeneratorMethod
+    AsyncMethod
+    AsyncGeneratorMethod 
+    get ClassElementName () { FunctionBody }
+    set ClassElementName ( PropertySetParameterList ) { FunctionBody }
+  
+  GeneratorMethod :
+    * ClassElementName ( UniqueFormalParameters ){GeneratorBody}
+
+  AsyncMethod :
+    async [no LineTerminator here] ClassElementName ( UniqueFormalParameters ) { AsyncFunctionBody }
+
+  AsyncGeneratorMethod :
+    async [no LineTerminator here]* ClassElementName ( UniqueFormalParameters ) { AsyncGeneratorBody }
+
+  ---
+
+  InitializeClassElements ( F, proto )
+
+  ...
+  5. For each item element in order from elements,
+    a. Assert: If element.[[Placement]] is "prototype" or "static", then element.[[Key]] is not a Private Name.
+    b. If element.[[Kind]] is "method" and element.[[Placement]] is "static" or "prototype",
+      i. Let receiver be F if element.[[Placement]] is "static", else let receiver be proto.
+      ii. Perform ? DefineClassElement(receiver, element).
+
+  InitializeInstanceElements ( O, constructor )
+
+  ...
+  3. Let elements be the value of F's [[Elements]] internal slot.
+  4. For each item element in order from elements,
+    a. If element.[[Placement]] is "own" and element.[[Kind]] is "method",
+      i. Perform ? DefineClassElement(O, element).
+
+  DefineClassElement (receiver, element)
+
+  ...
+  6. If key is a Private Name,
+    a. Perform ? PrivateFieldDefine(receiver, key, descriptor).
+
+  PrivateFieldDefine (P, O, desc)
+
+  ...
+  6. Append { [[PrivateName]]: P, [[PrivateFieldDescriptor]]: desc } to O.[[PrivateFieldDescriptors]].
+
+esid: prod-MethodDefinition
+---*/
+
+/***
+ * template notes:
+ * 1. method should always be #m
+ * 2. the template provides c.ref/other.ref for external reference
+ */
+
+function hasOwnProperty(obj, name) {
+  return Object.prototype.hasOwnProperty.call(obj, name);
+}
+
+var C = class {
+  /*{ element }*/
+
+  get ref() { return this.#m; }
+
+  constructor() {
+    assert.sameValue(
+      hasOwnProperty(this, '#m'), false,
+      'private methods are defined in an special internal slot and cannot be found as own properties'
+    );
+    assert.sameValue(typeof this.#m, 'function');
+    assert.sameValue(this.ref(), this.#m, 'returns the same value');
+
+    /*{ constructor }*/
+  }
+}
+
+var c = new C();
+var other = new C();
+
+assert.sameValue(
+  hasOwnProperty(C.prototype, '#m'), false,
+  'method is not defined in the prototype'
+);
+
+assert.sameValue(
+  hasOwnProperty(C, '#m'), false,
+  'method is not defined in the contructor'
+);
+
+assert.sameValue(
+  hasOwnProperty(c, '#m'), false,
+  'method cannot be seen outside of the class'
+);
+
+/***
+ * MethodDefinition : ClassElementName ( UniqueFormalParameters ) { FunctionBody }
+ * 
+ * 1. Let methodDef be DefineMethod of MethodDefinition with argument homeObject.
+ * ...
+ */
+assert.sameValue(c.ref, other.ref, 'The method is defined once, and reused on every new instance');
+
+/*{ assertions }*/
diff --git a/src/class-elements/prod-private-async-generator.case b/src/class-elements/prod-private-async-generator.case
new file mode 100644
index 0000000000..6c3c0299a7
--- /dev/null
+++ b/src/class-elements/prod-private-async-generator.case
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+desc: Private Async Generator
+template: private-methods
+features: [async-iteration]
+flags: [async]
+---*/
+
+//- setup
+var ctorPromise;
+
+//- element
+async * #m() { return 42; }
+
+//- constructor
+var ctorIter = this.#m();
+var p = ctorIter.next();
+ctorPromise = p.then(({ value, done }) => {
+    assert.sameValue(value, 42, 'return from generator method, inside ctor');
+    assert.sameValue(done, true, 'iterator is done, inside ctor');
+}, $DONE);
+assert.sameValue(this.#m.name, '#m', 'function name inside constructor');
+
+//- assertions
+assert.sameValue(c.ref.name, '#m', 'function name is preserved external reference');
+ctorPromise.then(() => {
+    var iter = c.ref();
+    return iter.next().then(({ value, done }) => {
+        assert.sameValue(value, 42, 'return from generator method');
+        assert.sameValue(done, true, 'iterator is done');
+    });
+}, $DONE).then($DONE, $DONE);
diff --git a/src/class-elements/prod-private-async-method.case b/src/class-elements/prod-private-async-method.case
new file mode 100644
index 0000000000..173575866b
--- /dev/null
+++ b/src/class-elements/prod-private-async-method.case
@@ -0,0 +1,29 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+desc: Private Async Method
+template: private-methods
+features: [async-functions]
+flags: [async]
+---*/
+
+//- setup
+var ctorPromise;
+
+//- element
+async #m() { return 42; }
+
+//- constructor
+assert.sameValue(this.#m.name, '#m', 'function name inside constructor');
+ctorPromise = this.#m().then(value => {
+    assert.sameValue(this.#m(), 42, 'already defined in the ctor');
+}, $DONE);
+
+//- assertions
+assert.sameValue(c.ref.name, '#m', 'function name is preserved external reference');
+ctorPromise.then(() => {
+    return c.ref().then(value => {
+        assert.sameValue(value, 42, 'function return');
+    });
+}, $DONE).then($DONE, $DONE);
diff --git a/src/class-elements/prod-private-generator.case b/src/class-elements/prod-private-generator.case
new file mode 100644
index 0000000000..5d5821a900
--- /dev/null
+++ b/src/class-elements/prod-private-generator.case
@@ -0,0 +1,23 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+desc: Private Generator
+template: private-methods
+features: [generators]
+---*/
+
+//- element
+* #m() { return 42; }
+
+//- constructor
+var res = this.#m().next();
+assert.sameValue(res.value, 42, 'return from generator method, inside ctor');
+assert.sameValue(res.done, true, 'iterator is done, inside ctor');
+assert.sameValue(this.#m.name, '#m', 'function name inside constructor');
+
+//- assertions
+var res = c.ref().next();
+assert.sameValue(res.value, 42, 'return from generator method');
+assert.sameValue(res.done, true, 'iterator is done');
+assert.sameValue(c.ref.name, '#m', 'function name is preserved external reference');
diff --git a/src/class-elements/prod-private-method.case b/src/class-elements/prod-private-method.case
new file mode 100644
index 0000000000..4e82ae18b7
--- /dev/null
+++ b/src/class-elements/prod-private-method.case
@@ -0,0 +1,18 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+desc: Private Method
+template: private-methods
+---*/
+
+//- element
+#m() { return 42; }
+
+//- constructor
+assert.sameValue(this.#m(), 42, 'already defined in the ctor');
+assert.sameValue(this.#m.name, '#m', 'function name inside constructor');
+
+//- assertions
+assert.sameValue(c.ref(), 42, 'function return');
+assert.sameValue(c.ref.name, '#m', 'function name is preserved external reference');
-- 
GitLab