Skip to content

Commit

Permalink
Generate fresh names for private members
Browse files Browse the repository at this point in the history
  • Loading branch information
LPTK committed Jan 23, 2025
1 parent 533a66c commit 973a2af
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 19 deletions.
10 changes: 8 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder:
case S(owner) =>
doc"${result(Value.This(owner))}.${
if (ts.k is syntax.LetBind) && !owner.isInstanceOf[semantics.TopLevelSymbol]
then "#" + ts.id.name
then "#" + owner.privatesScope.lookup_!(ts)
else ts.id.name
}"
case N => summon[Scope].lookup_!(ts)
Expand Down Expand Up @@ -157,12 +157,18 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder:
// * Note: `_pubFlds` is not used because in JS, fields are not declared
val clsParams = paramsOpt.fold(Nil)(_.paramSyms)
val ctorParams = clsParams.map(p => p -> scope.allocateName(p))
val privs =
val scp = isym.asInstanceOf[InnerSymbol].privatesScope
privFlds.map: fld =>
val nme = scp.allocateName(fld)
doc" # #$nme;"
.mkDocument(doc"")
val preCtorCode = ctorParams.foldLeft(body(preCtor)):
case (acc, (sym, nme)) =>
doc"$acc # this.${sym.name} = $nme;"
val ctorCode = doc"$preCtorCode${body(ctor)}"
val clsJS = doc"class ${sym.nme}${par.map(p => s" extends ${result(p)}").getOrElse("")} { #{ ${
privFlds.map(f => doc" # #${f.nme};").mkDocument(doc"")
privs
} # constructor(${
ctorParams.unzip._2.mkDocument(", ")
}) ${ braced(ctorCode) }${
Expand Down
4 changes: 3 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import scala.collection.mutable.{Set => MutSet}

import mlscript.utils.*, shorthands.*
import syntax.*
import hkmc2.utils.*

import Elaborator.State
import Tree.Ident
Expand Down Expand Up @@ -198,7 +199,8 @@ sealed trait ClassLikeSymbol extends Symbol:
* One overloaded `BlockMemberSymbol` may correspond to multiple `InnerSymbol`s
* A `Ref(_: InnerSymbol)` represents a `this`-like reference to the current object. */
// TODO prevent from appearing in Ref
sealed trait InnerSymbol extends Symbol:
sealed trait InnerSymbol(using State) extends Symbol:
val privatesScope: Scope = Scope.empty // * Scope for private members of this symbol
def subst(using SymbolSubst): InnerSymbol

class ClassSymbol(val tree: Tree.TypeDef, val id: Tree.Ident)(using State)
Expand Down
39 changes: 39 additions & 0 deletions hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,42 @@ fun main() =

// ——— ——— ———

// * Objects should not be allowed to have parameters...
// * Or these parameters should have default values.

:sjs
object Cls(x) with
fun huh = x
//│ JS (unsanitized):
//│ let Cls1;
//│ const Cls$class = class Cls {
//│ constructor(x1) {
//│ this.x = x1;
//│ }
//│ get huh() {
//│ return this.x;
//│ }
//│ toString() { return "Cls(" + this.x + ")"; }
//│ }; Cls1 = new Cls$class;
//│ Cls1.class = Cls$class;
//│ null

:e
Cls.x
//│ ╔══[ERROR] Object 'Cls' does not contain member 'x'
//│ ║ l.247: Cls.x
//│ ╙── ^^
//│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined'

:fixme
Cls.huh
//│ ═══[RUNTIME ERROR] Error: Access to required field 'huh' yielded 'undefined'

let c = new Cls(123)
//│ c = Cls { x: 123 }

c.x
//│ = 123

// ——— ——— ———

56 changes: 51 additions & 5 deletions hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,61 @@ Test.x
//│ = 1


:fixme
module Test with
let x = 1
let f = () => x
let x = 2
log(f())
//│ > let Test7;try { const Test$class2 = class Test { #x; #f; #x; constructor() { let selRes1, tmp, tmp1; this.#x = 1; this.#f = (...args) => { globalThis.Predef.checkArgs("", 0, true, args.length); return this.#x; }; this.#x = 2; selRes1 = globalThis.log; if (selRes1 === undefined) { throw new globalThis.Error("Access to required field 'log' yielded 'undefined'"); } else { tmp = selRes1; } tmp1 = this.#f() ?? null; tmp(tmp1) ?? null } toString() { return "Test"; } }; Test7 = new Test$class2; Test7.class = Test$class2; null } catch (e) { console.log('\u200B' + e + '\u200B'); }
//│ > ^
//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Identifier '#x' has already been declared
print(f())
//│ > 1


:sjs
class Cls(x) with
let x += 1
fun foo = x
let x *= 2
fun bar = x
print(this.x, x)
//│ JS (unsanitized):
//│ let Cls1;
//│ Cls1 = function Cls(x3) { return new Cls.class(x3); };
//│ Cls1.class = class Cls {
//│ #x;
//│ #x1;
//│ constructor(x2) {
//│ this.x = x2;
//│ let tmp, tmp1;
//│ tmp = this.x + 1;
//│ this.#x = tmp;
//│ tmp1 = this.#x * 2;
//│ this.#x1 = tmp1;
//│ Predef.print(this.x, this.#x1)
//│ }
//│ get foo() {
//│ return this.#x;
//│ }
//│ get bar() {
//│ return this.#x1;
//│ }
//│ toString() { return "Cls(" + this.x + ")"; }
//│ };
//│ null

let cls = Cls(10)
//│ > 10 22
//│ cls = Cls { x: 10 }

:expect 10
cls.x
//│ = 10

:expect 11
cls.foo
//│ = 11

:expect 22
cls.bar
//│ = 22


fun foo() =
Expand Down
17 changes: 6 additions & 11 deletions hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ foo()
//│ = 1



// FIXME should pick a different private field name for each `x`:



let x = 1
let f = () => x
//│ f = [Function: f]
Expand All @@ -33,9 +28,9 @@ f()
//│ = 1


// * Notice that we pick a different private field name for each `x`

:fixme
class Foo with
object Foo with
let x = 1
let f = () => x
fun g() = x
Expand All @@ -44,9 +39,9 @@ class Foo with
print(x)
print(f())
print(g())
//│ > let Foo1;try { Foo1 = class Foo { #x; #f; #x; constructor() { let tmp, tmp1, tmp2, tmp3, tmp4, tmp5; this.#x = 1; this.#f = (...args) => { globalThis.Predef.checkArgs("", 0, true, args.length); return this.#x; }; tmp = this.#f() ?? null; tmp1 = Predef.print(tmp); this.#x = 2; tmp2 = Predef.print(this.#x); tmp3 = this.#f() ?? null; tmp4 = Predef.print(tmp3); tmp5 = this.g(); Predef.print(tmp5) } g(...args) { globalThis.Predef.checkArgs("g", 0, true, args.length); return this.#x; } toString() { return "Foo"; } }; null } catch (e) { console.log('\u200B' + e + '\u200B'); }
//│ > ^
//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Identifier '#x' has already been declared

//│ > 1
//│ > 2
//│ > 1
//│ > 1


0 comments on commit 973a2af

Please sign in to comment.