[gjs] Class: improve prototype property descriptors



commit 9ec2adb40fc87ea58ce5006eb3449eb5a188909a
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sun Nov 20 13:18:34 2011 +0100

    Class: improve prototype property descriptors
    
    Use ES5 reflection methods to obtain and manipulate property
    descriptors, instead of copying the property values with for-in.
    This allows for non-enumerable properties, prevents Object.prototype
    properties from being iterated, allows setting .configurable, and
    most important allows accessor properties with the usual syntax.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=664437

 modules/lang.js      |   28 +++++++++++++++++++---------
 test/js/testClass.js |   39 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 57 insertions(+), 10 deletions(-)
---
diff --git a/modules/lang.js b/modules/lang.js
index 47bd7f2..695d448 100644
--- a/modules/lang.js
+++ b/modules/lang.js
@@ -1,3 +1,4 @@
+/* -*- mode: js; indent-tabs-mode: nil; -*- */
 // Copyright (c) 2008  litl, LLC
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -188,21 +189,30 @@ function Class(params) {
     let parent = params.Extends;
     if (!parent)
         parent = _Base;
+    let name = params.Name;
 
-    newClass.__super__ = parent;
-    newClass.prototype = Object.create(parent.prototype);
+    let propertyObj = { };
+    let propertyDescriptors = Object.getOwnPropertyNames(params).forEach(function(name) {
+        if (name == 'Name' || name == 'Extends')
+            return;
 
-    for (let prop in params) {
-        let value = params[prop];
+        let descriptor = Object.getOwnPropertyDescriptor(params, name);
 
-        if (typeof value === 'function')
-            value = wrapFunction(newClass, prop, value);
+        if (typeof descriptor.value === 'function')
+            descriptor.value = wrapFunction(newClass, name, descriptor.value);
 
-        newClass.prototype[prop] = value;
-    }
+        // we inherit writable and enumerable from the property
+        // descriptor of params (they're both true if created from an
+        // object literal)
+        descriptor.configurable = false;
 
+        propertyObj[name] = descriptor;
+    });
+
+    newClass.__super__ = parent;
+    newClass.prototype = Object.create(parent.prototype, propertyObj);
     newClass.prototype.constructor = newClass;
-    newClass.prototype.__name__ = params.Name;
+    newClass.prototype.__name__ = name;
     newClass.prototype.parent = _parent;
 
     return newClass;
diff --git a/test/js/testClass.js b/test/js/testClass.js
index efcea13..c5f5110 100644
--- a/test/js/testClass.js
+++ b/test/js/testClass.js
@@ -1,4 +1,4 @@
-// application/javascript;version=1.8
+// application/javascript;version=1.8 -*- mode: js; indent-tabs-mode: nil -*-
 
 const Lang = imports.lang;
 
@@ -50,6 +50,24 @@ const ToStringOverride = new Lang.Class({
     }
 });
 
+const Accessor = new Lang.Class({
+    Name: 'AccessorMagic',
+
+    _init: function(val) {
+        this._val = val;
+    },
+
+    get value() {
+        return this._val;
+    },
+
+    set value(val) {
+        if (val != 42)
+            throw TypeError('Value is not a magic number');
+        this._val = val;
+    }
+});
+
 function testClassFramework() {
     let newMagic = new MagicBase('A');
     assertEquals('A',  newMagic.a);
@@ -89,4 +107,23 @@ function testToString() {
     assertEquals('[object ToStringOverride]; hello', override.toString());
 }
 
+function testConfigurable() {
+    let newMagic = new MagicBase();
+
+    delete newMagic.foo;
+    assertNotUndefined(newMagic.foo);
+}
+
+function testAccessor() {
+    let newAccessor = new Accessor(11);
+
+    assertEquals(11, newAccessor.value);
+    assertRaises(function() {
+        newAccessor.value = 12;
+    });
+
+    newAccessor.value = 42;
+    assertEquals(42, newAccessor.value);
+}
+
 gjstestRun();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]