Howdy!

Find attached a patch that makes it so that the following ECMAscript
expressions all have the same effect:

Object();
new Object;
new Object();

This lays a foundation for having certain constructor functions (like
`Error') behave differently when invoked outside of a `new'
expression.

Andy, I'd particularly like your feedback on this approach, as it
relies in part on the use of `*program-wrappers*' (in `(language
ecmascript function)') and I'm not 100% sure I understand the intent
behind that table.


Regards,
Julian
From a748d47d47919affabac6102b5fa7ecc942aec5e Mon Sep 17 00:00:00 2001
From: Julian Graham <jool...@undecidable.net>
Date: Sat, 19 Nov 2016 10:28:50 -0500
Subject: [PATCH] ECMAscript: Bind type names to constructor functions in the
 global env.

Per ECMA-262: "The Object constructor is ... the initial value of the
Object property of the global object." More generally, this allows
constructor expressions to behave differently depending on whether they're
qualified with `new', which is important for types like `Error'.

* module/language/ecmascript/base.scm (new): Removed function. (Moved to
  impl.scm.)
* module/language/ecmascript/impl.scm: Export `js-module' (for unit testing
  support).
  (init-js-bindings!): Bind `Object' and `Array' to their respective
  constructors.
  (new): New function, relocated from base.scm. Modified to take a
  constructor function (rather than a type object) as its first argument.
* test-suite/tests/ecmascript.test: Import `(language ecmascript impl)' and
  call `js-init' to ensure that `Object' is visible at the top level.
  (compiler): Add test for `Object' binding type.
---
 module/language/ecmascript/base.scm | 13 +------------
 module/language/ecmascript/impl.scm | 21 +++++++++++++++------
 test-suite/tests/ecmascript.test    |  9 +++++++++
 3 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/module/language/ecmascript/base.scm b/module/language/ecmascript/base.scm
index fa6c85a..307145f 100644
--- a/module/language/ecmascript/base.scm
+++ b/module/language/ecmascript/base.scm
@@ -33,7 +33,7 @@
 
             call/this* call/this lambda/this define-js-method
 
-            new-object new))
+            new-object))
 
 (define-class <undefined> ())
 
@@ -238,14 +238,3 @@
          pairs)
     o))
 (slot-set! *object-prototype* 'constructor new-object)
-
-(define-method (new o . initargs)
-  (let ((ctor (js-constructor o)))
-    (if (not ctor)
-        (throw 'TypeError 'new o)
-        (let ((o (make <js-object>
-                   #:prototype (or (js-prototype o) *object-prototype*))))
-          (let ((new-o (call/this o apply ctor initargs)))
-            (if (is-a? new-o <js-object>)
-                new-o
-                o))))))
diff --git a/module/language/ecmascript/impl.scm b/module/language/ecmascript/impl.scm
index 27c077a..56674d0 100644
--- a/module/language/ecmascript/impl.scm
+++ b/module/language/ecmascript/impl.scm
@@ -26,15 +26,15 @@
   #:re-export (*undefined* *this* call/this*
                pget pput pdel has-property?
                ->boolean ->number
-               new-object new new-array)
-  #:export (js-init get-this
+               new-object new-array)
+  #:export (js-init get-this js-module
             typeof
             bitwise-not logical-not
             shift
             mod
             band bxor bior
-            make-enumerator))
-
+            make-enumerator
+            new))
 
 (define-class <js-module-object> (<js-object>)
   (module #:init-form (current-module) #:init-keyword #:module
@@ -78,8 +78,8 @@
   ;; decodeURI, decodeURIComponent, encodeURI, encodeURIComponent
   ;; Object Function Array String Boolean Number Date RegExp Error EvalError
   ;; RangeError ReferenceError SyntaxError TypeError URIError
-  (module-define! mod 'Object *object-prototype*)
-  (module-define! mod 'Array *array-prototype*))
+  (module-define! mod 'Object (slot-ref *object-prototype* 'constructor))
+  (module-define! mod 'Array (slot-ref *array-prototype* 'constructor)))
 
 (define (js-init)
   (cond ((get-this))
@@ -167,3 +167,12 @@
     (apply new-array (filter (lambda (p)
                                (not (prop-has-attr? obj p 'DontEnum)))
                              (hash-map->list (lambda (k v) k) props)))))
+
+(define-method (new ctor . initargs)
+  (let ((this (make <js-object>
+                #:prototype (or (hashq-ref *program-wrappers* ctor)
+                                *object-prototype*))))
+    (let ((new-o (call/this this apply ctor initargs)))
+      (if (is-a? new-o <js-object>)
+          new-o
+          this))))
diff --git a/test-suite/tests/ecmascript.test b/test-suite/tests/ecmascript.test
index 9f2731e..3490a9a 100644
--- a/test-suite/tests/ecmascript.test
+++ b/test-suite/tests/ecmascript.test
@@ -18,9 +18,12 @@
 
 (define-module (test-ecmascript)
   #:use-module (test-suite lib)
+  #:use-module (language ecmascript impl)
   #:use-module (language ecmascript parse)
   #:use-module ((system base compile) #:select (compile)))
 
+(js-init) ; Create bindings in global environment
+
 
 (define (eread str)
   (call-with-input-string str read-ecmascript))
@@ -89,6 +92,12 @@
                    #:from 'ecmascript
                    #:to 'tree-il)))) ; Can't reference `Object' as value here
 
+  (pass-if "typeof Object;"
+    (equal? "function"
+           (compile (call-with-input-string "typeof Object;" read-ecmascript)
+                    #:env (js-module (get-this)) ; env with `Object' defined
+                    #:from 'ecmascript)))
+
   ;; FIXME: Broken!
   ;; (ecompile "[1,2,3,4].map(function(x) { return x * x; });"
   ;;           '(1 4 9 16))
-- 
2.10.2

Reply via email to