Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Object.create(canvas.getContext())

57 views
Skip to first unread message

Jan Bruns

unread,
Nov 12, 2017, 5:09:11 AM11/12/17
to

Hallo.

What's wrong here?

My intention was to create an object that inherits from a specific webgl
context, so that I can populate some (more or less) webgl-related, app-
specific helper functions.


gl = Object.create( webglcanvas.getContext("webgl2") );

dolog(gl); // prints "[object Object]"

dolog(gl.__proto__); // prints "[object WebGL2RenderingContext]"

gl.clearColor(0.7, 0.7, 0.7, 1.0);


TypeError: 'clearColor' called on an object that does not implement
interface WebGL2RenderingContext.

Gruss

Jan Bruns

Thomas 'PointedEars' Lahn

unread,
Nov 12, 2017, 11:42:48 AM11/12/17
to
dolog() is not a built-in function in any ECMAScript implementations that
I know. That leaves one to *guess* what *your* dolog(…) does (presumably,
“({}).toString.call(…)”.)

That you can create an object which inherits from another does not mean that
you can use it like the other; this is particulary true of host objects as
this one. If subclassing does not work either (or cannot be assumed to be
available), create a wrapper object. If available, you can use “Proxy”
(ECMAScript 2015+) or “__noSuchMethod__” (Mozilla JavaScript 43+ only,
obsolete) to implement seamless access.

--
PointedEars
FAQ: <http://PointedEars.de/faq> | <http://PointedEars.de/es-matrix>
<https://github.com/PointedEars> | <http://PointedEars.de/wsvn/>
Twitter: @PointedEars2 | Please do not cc me./Bitte keine Kopien per E-Mail.

Thomas 'PointedEars' Lahn

unread,
Nov 12, 2017, 12:45:37 PM11/12/17
to
Jan Bruns wrote:

> dolog(gl.__proto__); // prints "[object WebGL2RenderingContext]"

The standards-compliant approach is

Object.getPrototypeOf(gl)

instead of

gl.__proto__

It is available since ECMAScript Edition 5.0 (2009-12).

<http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262%205th%20edition%20December%202009.pdf#page=122>

<http://www.ecma-international.org/ecma-262/8.0/index.html#sec-object.getprototypeof>

Jan Bruns

unread,
Nov 14, 2017, 6:48:40 AM11/14/17
to
Thomas 'PointedEars' Lahn:

> That you can create an object which inherits from another does not mean
> that you can use it like the other;

Obviously.

> this is particulary true of host
> objects as this one. If subclassing does not work either (or cannot be
> assumed to be available), create a wrapper object.

That's what I wanted to avoid.

Open GL, GL ES, and webgl are all APIs that hide obejct oriented
programming aspects by some state mechanism. For example, to change some
object's property often means to first set the object reference to some
"current object" state using some API method, to finally change the
property of that "current object" using some other API method.

This is beacause initial OpenGL was focused on plain C usage, I guess.

Wrapping all the webgl methods explicitly just to get some functionality
that the prototype chain and the API's host code could do doesn't sound
very attractive either, so I'll decide to put the burden to distungish
native API calls from helper API calls on some higher level helper API.

> If available, you
> can use “Proxy” (ECMAScript 2015+) or “__noSuchMethod__” (Mozilla
> JavaScript 43+ only, obsolete) to implement seamless access.

Didn't know about that, but currently looks like much of browser
specifics that don't have anything to do with webgl.

Gruss

Jan Bruns




Thomas 'PointedEars' Lahn

unread,
Nov 14, 2017, 8:32:24 PM11/14/17
to
Jan Bruns wrote:

> Thomas 'PointedEars' Lahn:
>> […] this is particulary true of host objects as this one. If subclassing
>> does not work either (or cannot be assumed to be available), create a
>> wrapper object.
>
> That's what I wanted to avoid.
>
> Open GL, GL ES, and webgl are all APIs that hide obejct oriented
> programming aspects by some state mechanism. For example, to change some
> object's property often means to first set the object reference to some
> "current object" state using some API method, to finally change the
> property of that "current object" using some other API method.
>
> This is beacause initial OpenGL was focused on plain C usage, I guess.

WebGL is an *object-oriented* API based on OpenGL ES 2.0, so I do not see
your point.

> Wrapping all the webgl methods explicitly just to get some functionality
> that the prototype chain and the API's host code could do doesn't sound
> very attractive either, so I'll decide to put the burden to distungish
> native API calls from helper API calls on some higher level helper API.

Your choice. I see that subclassing does not work in this case.

But it appears to be rather easy to create such a wrapper, at least in
Chromium 57:

var my = (function () {
var canvas = document.createElement("canvas");
var context = canvas.getContext("webgl");
var proto = Object.getPrototypeOf(context);

function WebGLWrapper (context)
{
this.context = context;
}

/* Wrap built-in methods */
Object.assign(WebGLWrapper.prototype, (function () {
var wrappedMethods = Object.create(null);

// Object.keys(proto).forEach(function (key) {
Object.keys(proto).forEach((key) => {
try
{
if (typeof proto[key] == "function")
{
wrappedMethods[key] = (function (freeKey) {
return function (...args) {
// this.context[freeKey].apply(this.context, arguments);
return this.context[freeKey](...args);
};
}(key));
}
}
catch (e) {}
});

return wrappedMethods;
}()));

Object.assign(WebGLWrapper.prototype, {
/* custom methods */
});

return {
/** @memberOf my */
WebGLWrapper: WebGLWrapper
};
}());

// gl = new my.WebGLWrapper(gl);

Barely tested.

>> If available, you can use “Proxy” (ECMAScript 2015+) or
>> “__noSuchMethod__” (Mozilla JavaScript 43+ only, obsolete) to implement
>> seamless access.
>
> Didn't know about that, but currently looks like much of browser
> specifics

WebGL is browser-specific. “Proxy” is not, in general. However, Internet
Explorer supports WebGL, but its JScript version does not implement either
“Proxy” or “__noSuchMethod__”, which might be a showstopper for that
approach.

> that don't have anything to do with webgl.

Yes, it is a general approach. Your point being?

Jan Bruns

unread,
Nov 17, 2017, 3:25:44 PM11/17/17
to

Thomas 'PointedEars' Lahn:
> Jan Bruns wrote:

>> Open GL, GL ES, and webgl are all APIs that hide obejct oriented
>> programming aspects by some state mechanism. For example, to change
>> some object's property often means to first set the object reference to
>> some "current object" state using some API method, to finally change
>> the property of that "current object" using some other API method.

>> This is beacause initial OpenGL was focused on plain C usage, I guess.

> WebGL is an *object-oriented* API based on OpenGL ES 2.0, so I do not
> see your point.

?

[...]
> // gl = new my.WebGLWrapper(gl);
> Barely tested.

Thanks for taking to time to post some probably solution.

I didn't really try that out, but after doing some more webGL related
stuff, the prob in question turned out to become more important than I
expected, and after some investigation, I came up with some code around
this idea:

this.define_method = function(nam,cobj,cfun){

this[nam] = function() { return cfun.apply(cobj,arguments); };

};

this.inherit_methods = function(o) {

for (var i in o) { if (typeof(o[i])=="function") {

this.define_method(i,o,o[i]);

};};};


this.inherit_methods(gl);

Looks much more human readable to me.


>> that don't have anything to do with webgl.
> Yes, it is a general approach. Your point being?

?

Gruss

Jan Bruns


Thomas 'PointedEars' Lahn

unread,
Nov 18, 2017, 11:48:11 PM11/18/17
to
Jan Bruns wrote:

> this.define_method = function(nam,cobj,cfun){
>
> this[nam] = function() { return cfun.apply(cobj,arguments); };
>
> };
>
> this.inherit_methods = function(o) {
>
> for (var i in o) { if (typeof(o[i])=="function") {
>
> this.define_method(i,o,o[i]);
>
> };};};
>
>
> this.inherit_methods(gl);
>
> Looks much more human readable to me.

YMMV. But your code is also much more error-prone (and lacks code style).

For example, there is a reason why I enclosed the host object property
access in a “try” block.

>>> that don't have anything to do with webgl.
>> Yes, it is a general approach. Your point being?
>
> ?

I do not understand that question.

Jan Bruns

unread,
Nov 19, 2017, 4:40:06 PM11/19/17
to

Thomas 'PointedEars' Lahn:
> Jan Bruns wrote:

>> this.define_method = function(nam,cobj,cfun){
>>
>> this[nam] = function() { return cfun.apply(cobj,arguments); };
>>
>> };
>>
>> this.inherit_methods = function(o) {
>>
>> for (var i in o) { if (typeof(o[i])=="function") {
>>
>> this.define_method(i,o,o[i]);
>>
>> };};};
>>
>>
>> this.inherit_methods(gl);
>>
>> Looks much more human readable to me.

> YMMV. But your code is also much more error-prone

Well, it comes with useful flexibility.

For example, some webgl extensions come with additional methods that have
been adopted to webgl2.

With webgl1 they're used like

var e = gl.getExtension(some_name);

e.someMethod_suffix(); // call some extension's method

whereas in webgl2, they've become part of the API:

gl.someMethod();

So it'll simply be best to load the extension with:

var e = gl.getExtension(some_name);
gl.define_method("someMethod",e,e.someMethod_suffix);


> (and lacks code style).

Style? My newsreader is broken (I think). Somehow linebreaks get lost
somewhere, probably to get some format-flowed styling.

> For example, there is a reason why I enclosed the host object property
> access in a “try” block.

What is that reason? Does "typeof" throw on any enumerated property
anywhere?


Gruss

Jan Bruns

Thomas 'PointedEars' Lahn

unread,
Nov 19, 2017, 5:33:02 PM11/19/17
to
Jan Bruns wrote:

> Thomas 'PointedEars' Lahn:
>> Jan Bruns wrote:
>>> this.define_method = function(nam,cobj,cfun){
>>>
>>> this[nam] = function() { return cfun.apply(cobj,arguments); };
>>>
>>> };
>>>
>>> this.inherit_methods = function(o) {
>>>
>>> for (var i in o) { if (typeof(o[i])=="function") {
>>>
>>> this.define_method(i,o,o[i]);
>>>
>>> };};};
>>>
>>>
>>> this.inherit_methods(gl);
>>>
>>> Looks much more human readable to me.
>
>> YMMV. But your code is also much more error-prone
>
> Well, it comes with useful flexibility.

You misunderstand. I had not criticized your approach as I understood it
then, but your implementation of it.

> So it'll simply be best to load the extension with:
>
> var e = gl.getExtension(some_name);
> gl.define_method("someMethod",e,e.someMethod_suffix);

Probably not. ISTM now that you are trying to augment a host object, which
is a recipe for disaster.

>> (and lacks code style).
>
> Style? My newsreader is broken (I think). Somehow linebreaks get lost
> somewhere, probably to get some format-flowed styling.

Perhaps.

>> For example, there is a reason why I enclosed the host object property
>> access in a “try” block.
>
> What is that reason? Does "typeof" throw on any enumerated property
> anywhere?

If you would have tried, you would have seen that

typeof Object.getPrototypeOf(webGLcontext)[name] == "function"

throws “TypeError: Illegal invocation” for

name === "canvas"

name === "drawingBufferWidth"

and

name === "drawingBufferHeight"

in Chrome 57 (navigator.userAgent === "Mozilla/5.0 (X11; Linux x86_64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36").

You need to realize that those are *host* objects, and all bets are off.

Thomas 'PointedEars' Lahn

unread,
Nov 19, 2017, 5:34:24 PM11/19/17
to
Jan Bruns wrote:

> Thomas 'PointedEars' Lahn:
>> Jan Bruns wrote:
>>> this.define_method = function(nam,cobj,cfun){
>>>
>>> this[nam] = function() { return cfun.apply(cobj,arguments); };
>>>
>>> };
>>>
>>> this.inherit_methods = function(o) {
>>>
>>> for (var i in o) { if (typeof(o[i])=="function") {
>>>
>>> this.define_method(i,o,o[i]);
>>>
>>> };};};
>>>
>>>
>>> this.inherit_methods(gl);
>>>
>>> Looks much more human readable to me.
>
>> YMMV. But your code is also much more error-prone
>
> Well, it comes with useful flexibility.

You misunderstand. I had not criticized your approach as I understood it
then, but your implementation of it.

> So it'll simply be best to load the extension with:
>
> var e = gl.getExtension(some_name);
> gl.define_method("someMethod",e,e.someMethod_suffix);

Probably not. ISTM now that you are trying to augment a host object, which
is a recipe for disaster.

>> (and lacks code style).
>
> Style? My newsreader is broken (I think). Somehow linebreaks get lost
> somewhere, probably to get some format-flowed styling.

Perhaps.

>> For example, there is a reason why I enclosed the host object property
>> access in a “try” block.
>
> What is that reason? Does "typeof" throw on any enumerated property
> anywhere?

If you would have tried, you would have seen that

typeof Object.getPrototypeOf(webGLcontext)[name] == "function"

throws “TypeError: Illegal invocation” for the _enumerable_ properties where

name === "canvas"

name === "drawingBufferWidth"

and

name === "drawingBufferHeight"

in Chrome 57 (navigator.userAgent === "Mozilla/5.0 (X11; Linux x86_64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36").

You need to realize that those are *host* objects, and all bets are off.

Jan Bruns

unread,
Nov 20, 2017, 9:47:22 AM11/20/17
to
Thomas 'PointedEars' Lahn:
> Jan Bruns wrote:
>

>>> For example, there is a reason why I enclosed the host object property
>>> access in a “try” block.

>> What is that reason? Does "typeof" throw on any enumerated property
>> anywhere?

> If you would have tried, you would have seen that
>
> typeof Object.getPrototypeOf(webGLcontext)[name] == "function"
>
> throws “TypeError: Illegal invocation” for the _enumerable_ properties
> where

Interesting. Saved me from thinking about how to create an object that
throws on typeof(enumerated property). Maybe the "getters" can do so.

But we're talking about a very popular API, meaning there are thousands
or maybe even a million programmers out there that tried out or will try
out working with it.

The prototype object of such an API of course might have very browser
specific behaviour that isn't spcificly user friendly (and even doesn't
necessarily have to meet programing language specific functionality), as
long as the API's specification doesn't explicitly define such behaviour.

But if

for (var i in API) { typeof API[i] };

would throw, why did they implement and enumerate that fail?

Well, probably for the same reasons, as

Object.create(API);

might not work as expected: work in progress.

But then, the only clean way to deal with it would be to copy&paste the
API specification (like C headers) to any project that needs to inherit
the API instead of just referencing it. I'll probably do that in case I
see a problem that could be solved that way, but work in progress...

Gruss

Jan Bruns





Thomas 'PointedEars' Lahn

unread,
Nov 20, 2017, 10:07:35 PM11/20/17
to
Jan Bruns wrote:

> Thomas 'PointedEars' Lahn:
>> Jan Bruns wrote:
>>>> For example, there is a reason why I enclosed the host object property
>>>> access in a “try” block.
>>> What is that reason? Does "typeof" throw on any enumerated property
>>> anywhere?
>> If you would have tried, you would have seen that
>>
>> typeof Object.getPrototypeOf(webGLcontext)[name] == "function"
>>
>> throws “TypeError: Illegal invocation” for the _enumerable_ properties
>> where
>
> Interesting. Saved me from thinking about how to create an object that
> throws on typeof(enumerated property). Maybe the "getters" can do so.

Most certainly. (Why the quotation marks?)

> But we're talking about a very popular API, meaning there are thousands
> or maybe even a million programmers out there that tried out or will try
> out working with it.

The generally recommended approach is the one that I recommended to you
initially: create a wrapper for the host object. Obtaining, and operating
on, the keys of the object is just one of the more convenient
implementations of that approach.

> The prototype object of such an API of course might have very browser
> specific behaviour that isn't spcificly user friendly (and even doesn't
> necessarily have to meet programing language specific functionality), as
> long as the API's specification doesn't explicitly define such behaviour.
>
> But if
>
> for (var i in API) { typeof API[i] };
>
> would throw, why did they implement and enumerate that fail?

I repeat, you must differentiate between the programming language and the
APIs that can be used with it (see also the ECMAScript Support Matrix
referred to in my signature). The “typeof” operator is a *language*
feature. It was devised long before the host runtime environments were
conceived in which, and the APIs with which, it can potentially be used.
So in general, it is supposed to work with *native* objects; it may or may
not work with host objects such as WebGL context objects and their
prototypes. Also, because ECMAScript was an *afterthought*, the Language
Specification *specifies* the possibility for certain aberrant behavior
for host objects. This includes the “typeof” operation:

<http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,
%201st%20edition,%20June%201997.pdf>, § 11.4.3

<http://www.ecma-international.org/ecma-262/8.0/#sec-typeof-operator>

However, further testing in the same environment shows that only access to
some of the *prototype* properties fails, presumably because their getter
cannot handle access that is not to a properly initialized instance. This
would mean that a for-in statement on the instance is the only way, if less
efficient, to solve this using iteration with sufficient completeness.
Still, ISTM that the “try” block is mandatory in such a case as there are
other examples where accessing, including even only testing, the properties
of a non-prototype host object fails.

It is also possible that this prototype is designed to be extended, in which
case the iteration is unnecessary, and you can simply add the custom methods
to the prototype, so that they can be inherited by the instance. Simple
tests in the same environment support this assumption. Doing so would
certainly be safer than augmenting the instance itself (which I cannot be
sure from the code that you posted that you attempted it), but it needs more
testing.

> Well, probably for the same reasons, as
>
> Object.create(API);
>
> might not work as expected: work in progress.

I do not think so.

> But then, the only clean way to deal with it would be to copy&paste the
> API specification (like C headers) to any project that needs to inherit
> the API instead of just referencing it. […]

Not at all.

Jan Bruns

unread,
Nov 21, 2017, 11:23:52 AM11/21/17
to

Thomas 'PointedEars' Lahn:
> Jan Bruns wrote:

>> But we're talking about a very popular API, meaning there are thousands
>> or maybe even a million programmers out there that tried out or will
>> try out working with it.

> The generally recommended approach is the one that I recommended to you
> initially: create a wrapper for the host object. Obtaining, and
> operating on, the keys of the object is just one of the more convenient
> implementations of that approach.

Your approach was to use the API's prototype to ask for valid API keys.
Noone can tell if that's a host object anywhere. The API itself could be
a host object, too, and isn't required to have a prototype object (so I
can't expected it to list all the API methods).

The only thing that is known for sure is that it has some specific
properties that can be read from normal script code, and some specific
methods that can be called that way.

API[some_specified_property];
API[some_specified_method](parameterlist);

Maybe there are js scripting engines that theoretically allow such an API
to fail on:

API[some_specified_method].apply(API,parameterlist);

or even more fundamental:

var f=API[some_specified_method];



You already noticed that the line of code

if (typeof proto[key] == "function")

throws exceptions. So what makes you think that's a more secure approach
than asking the API object itself to enumerate it's methods?

> However, further testing in the same environment shows that only access
> to some of the *prototype* properties fails, presumably because their
> getter cannot handle access that is not to a properly initialized
> instance. This would mean that a for-in statement on the instance is
> the only way, if less efficient, to solve this using iteration with
> sufficient completeness. Still, ISTM that the “try” block is mandatory
> in such a case as there are other examples where accessing, including
> even only testing, the properties of a non-prototype host object fails.

No. If I see that for-loop fail I'll drop in the method list from the API
specification.

> It is also possible that this prototype is designed to be extended, in
> which case the iteration is unnecessary, and you can simply add the
> custom methods to the prototype, so that they can be inherited by the
> instance. Simple tests in the same environment support this assumption.
> Doing so would certainly be safer than augmenting the instance itself
> (which I cannot be sure from the code that you posted that you attempted
> it), but it needs more testing.

Ah. Hehe. No. My code doesn't do anthing with the API object except
asking for enumerated keys and reading and callig the methods with API
bound to "this".

Gruss

Jan Bruns

Thomas 'PointedEars' Lahn

unread,
Nov 21, 2017, 10:13:06 PM11/21/17
to
Jan Bruns wrote:

> Thomas 'PointedEars' Lahn:
>> Jan Bruns wrote:
>>> But we're talking about a very popular API, meaning there are thousands
>>> or maybe even a million programmers out there that tried out or will
>>> try out working with it.
>
>> The generally recommended approach is the one that I recommended to you
>> initially: create a wrapper for the host object. Obtaining, and
>> operating on, the keys of the object is just one of the more convenient
>> implementations of that approach.
>
> Your approach was to use the API's prototype to ask for valid API keys.
> Noone can tell if that's a host object anywhere.

This is why in iteration try-catch should be used, too.

> The API itself could be a host object, too, and isn't required to have a
> prototype object (so I can't expected it to list all the API methods).

Sadly, you have no clue what you are talking about.

> [further misconceptions]
0 new messages