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