unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* [PATCH] ECMAscript: Bind type names to constructor functions in the global env.
@ 2016-11-19 15:44 Julian Graham
  2017-01-09  2:28 ` Julian Graham
  2017-01-09 21:20 ` Andy Wingo
  0 siblings, 2 replies; 4+ messages in thread
From: Julian Graham @ 2016-11-19 15:44 UTC (permalink / raw)
  To: guile-devel

[-- Attachment #1: Type: text/plain, Size: 533 bytes --]

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

[-- Attachment #2: 0001-ECMAscript-Bind-type-names-to-constructor-functions-.patch --]
[-- Type: text/x-patch, Size: 5162 bytes --]

From a748d47d47919affabac6102b5fa7ecc942aec5e Mon Sep 17 00:00:00 2001
From: Julian Graham <joolean@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
+
 \f
 (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


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] ECMAscript: Bind type names to constructor functions in the global env.
  2016-11-19 15:44 [PATCH] ECMAscript: Bind type names to constructor functions in the global env Julian Graham
@ 2017-01-09  2:28 ` Julian Graham
  2017-01-09 21:20 ` Andy Wingo
  1 sibling, 0 replies; 4+ messages in thread
From: Julian Graham @ 2017-01-09  2:28 UTC (permalink / raw)
  To: guile-devel; +Cc: Andy Wingo

Hey Andy,

If you're looking at patches, would you look this over and merge it if
you like it? Doesn't have to go into 2.1.6 per se, but it would be
cool to have it on master.


On Sat, Nov 19, 2016 at 10:44 AM, Julian Graham <joolean@gmail.com> wrote:
> 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.



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] ECMAscript: Bind type names to constructor functions in the global env.
  2016-11-19 15:44 [PATCH] ECMAscript: Bind type names to constructor functions in the global env Julian Graham
  2017-01-09  2:28 ` Julian Graham
@ 2017-01-09 21:20 ` Andy Wingo
  2017-01-17  1:23   ` Julian Graham
  1 sibling, 1 reply; 4+ messages in thread
From: Andy Wingo @ 2017-01-09 21:20 UTC (permalink / raw)
  To: Julian Graham; +Cc: guile-devel

On Sat 19 Nov 2016 16:44, Julian Graham <joolean@gmail.com> writes:

> 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.

I am happy to include patches like this one.

Yet -- *program-wrappers*, what is that about?  I don't even remember
any more.  I guess it's for setting a property on a function instance,
even if the function is a normal Guile function?  I guess I am skeptical
given that the hash table is doubly weak.

Please don't assume that the existing code is good or even correct :) We
have all learned many things since 2009 -- this flip side of that being
that I know I was much more ignorant back then :)

Andy



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] ECMAscript: Bind type names to constructor functions in the global env.
  2017-01-09 21:20 ` Andy Wingo
@ 2017-01-17  1:23   ` Julian Graham
  0 siblings, 0 replies; 4+ messages in thread
From: Julian Graham @ 2017-01-17  1:23 UTC (permalink / raw)
  To: Andy Wingo; +Cc: guile-devel

On Mon, Jan 9, 2017 at 4:20 PM, Andy Wingo <wingo@pobox.com> wrote:
> I am happy to include patches like this one.
>
> Yet -- *program-wrappers*, what is that about?  I don't even remember
> any more.  I guess it's for setting a property on a function instance,
> even if the function is a normal Guile function?  I guess I am skeptical
> given that the hash table is doubly weak.

My read is that it's an explicit bridge between a normal Guile
function and a more ECMAScript-y function object representation of
that function. That is to say, an attempt to have one's cake
(functions that act like other JS objects) and eat it, too ("native"
function application). If Guile's ES implementation continues to
develop (and I hope it does!) a good reason may present itself that ES
Functions can't continue to also be native Guile functions under the
hood. But if we agree on the idea behind that table, I don't see why
that change has to happen now.


> Please don't assume that the existing code is good or even correct :) We
> have all learned many things since 2009 -- this flip side of that being
> that I know I was much more ignorant back then :)

Well, I think it works, and it's pretty cheap. Per the above, I think
the alternative is to redesign the ES functions implementation in a
way that makes functions look a lot more like objects, such that they
can no longer be applied "directly." But I was hoping to defer doing
that until the core object implementation and of the behavior of
prototypes was a bit further along, with more test coverage, etc. I
don't know. What do you think?



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2017-01-17  1:23 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-11-19 15:44 [PATCH] ECMAscript: Bind type names to constructor functions in the global env Julian Graham
2017-01-09  2:28 ` Julian Graham
2017-01-09 21:20 ` Andy Wingo
2017-01-17  1:23   ` Julian Graham

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).