Skip to content
This repository was archived by the owner on Mar 13, 2018. It is now read-only.

Commit 76d2910

Browse files
author
Scott J. Miles
committed
prevent stackoverflow when calling a non-existent superclass method
1 parent 8a6e03a commit 76d2910

File tree

1 file changed

+129
-122
lines changed

1 file changed

+129
-122
lines changed

src/lib/super.js

+129-122
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,129 @@
1-
/*
2-
* Copyright 2013 The Polymer Authors. All rights reserved.
3-
* Use of this source code is governed by a BSD-style
4-
* license that can be found in the LICENSE file.
5-
*/
6-
(function(scope) {
7-
// super
8-
9-
// `arrayOfArgs` is an optional array of args like one might pass
10-
// to `Function.apply`
11-
12-
// TODO(sjmiles):
13-
// $super must be installed on an instance or prototype chain
14-
// as `super`, and invoked via `this`, e.g.
15-
// `this.super();`
16-
17-
// will not work if function objects are not unique, for example,
18-
// when using mixins.
19-
// The memoization strategy assumes each function exists on only one
20-
// prototype chain i.e. we use the function object for memoizing)
21-
// perhaps we can bookkeep on the prototype itself instead
22-
function $super(arrayOfArgs) {
23-
// since we are thunking a method call, performance is important here:
24-
// memoize all lookups, once memoized the fast path calls no other
25-
// functions
26-
//
27-
// find the caller (cannot be `strict` because of 'caller')
28-
var caller = $super.caller;
29-
// memoized 'name of method'
30-
var nom = caller.nom;
31-
// memoized next implementation prototype
32-
var _super = caller._super;
33-
if (!_super) {
34-
if (!nom) {
35-
nom = caller.nom = nameInThis.call(this, caller);
36-
}
37-
if (!nom) {
38-
console.warn('called super() on a method not installed declaratively (has no .nom property)');
39-
}
40-
// super prototype is either cached or we have to find it
41-
// by searching __proto__ (at the 'top')
42-
_super = memoizeSuper(caller, nom, getPrototypeOf(this));
43-
}
44-
if (!_super) {
45-
// if _super is falsey, there is no super implementation
46-
//console.warn('called $super(' + nom + ') where there is no super implementation');
47-
} else {
48-
// our super function
49-
var fn = _super[nom];
50-
// memoize information so 'fn' can call 'super'
51-
if (!fn._super) {
52-
memoizeSuper(fn, nom, _super);
53-
}
54-
// invoke the inherited method
55-
// if 'fn' is not function valued, this will throw
56-
return fn.apply(this, arrayOfArgs || []);
57-
}
58-
}
59-
60-
function nextSuper(proto, name, caller) {
61-
// look for an inherited prototype that implements name
62-
while (proto) {
63-
if ((proto[name] !== caller) && proto[name]) {
64-
return proto;
65-
}
66-
proto = getPrototypeOf(proto);
67-
}
68-
}
69-
70-
function memoizeSuper(method, name, proto) {
71-
// find and cache next prototype containing `name`
72-
// we need the prototype so we can do another lookup
73-
// from here
74-
method._super = nextSuper(proto, name, method);
75-
if (method._super) {
76-
// _super is a prototype, the actual method is _super[name]
77-
// tag super method with it's name for further lookups
78-
method._super[name].nom = name;
79-
}
80-
return method._super;
81-
}
82-
83-
function nameInThis(value) {
84-
var p = this.__proto__;
85-
while (p && p !== HTMLElement.prototype) {
86-
// TODO(sjmiles): getOwnPropertyNames is absurdly expensive
87-
var n$ = Object.getOwnPropertyNames(p);
88-
for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
89-
var d = Object.getOwnPropertyDescriptor(p, n);
90-
if (typeof d.value === 'function' && d.value === value) {
91-
return n;
92-
}
93-
}
94-
p = p.__proto__;
95-
}
96-
}
97-
98-
// NOTE: In some platforms (IE10) the prototype chain is faked via
99-
// __proto__. Therefore, always get prototype via __proto__ instead of
100-
// the more standard Object.getPrototypeOf.
101-
function getPrototypeOf(prototype) {
102-
return prototype.__proto__;
103-
}
104-
105-
// utility function to precompute name tags for functions
106-
// in a (unchained) prototype
107-
function hintSuper(prototype) {
108-
// tag functions with their prototype name to optimize
109-
// super call invocations
110-
for (var n in prototype) {
111-
var pd = Object.getOwnPropertyDescriptor(prototype, n);
112-
if (pd && typeof pd.value === 'function') {
113-
pd.value.nom = n;
114-
}
115-
}
116-
}
117-
118-
// exports
119-
120-
scope.super = $super;
121-
122-
})(Polymer);
1+
/*
2+
* Copyright 2013 The Polymer Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style
4+
* license that can be found in the LICENSE file.
5+
*/
6+
(function(scope) {
7+
// super
8+
9+
// `arrayOfArgs` is an optional array of args like one might pass
10+
// to `Function.apply`
11+
12+
// TODO(sjmiles):
13+
// $super must be installed on an instance or prototype chain
14+
// as `super`, and invoked via `this`, e.g.
15+
// `this.super();`
16+
17+
// will not work if function objects are not unique, for example,
18+
// when using mixins.
19+
// The memoization strategy assumes each function exists on only one
20+
// prototype chain i.e. we use the function object for memoizing)
21+
// perhaps we can bookkeep on the prototype itself instead
22+
function $super(arrayOfArgs) {
23+
// since we are thunking a method call, performance is important here:
24+
// memoize all lookups, once memoized the fast path calls no other
25+
// functions
26+
//
27+
// find the caller (cannot be `strict` because of 'caller')
28+
var caller = $super.caller;
29+
// memoized 'name of method'
30+
var nom = caller.nom;
31+
// memoized next implementation prototype
32+
var _super = caller._super;
33+
if (!_super) {
34+
if (!nom) {
35+
nom = caller.nom = nameInThis.call(this, caller);
36+
}
37+
if (!nom) {
38+
console.warn('called super() on a method not installed declaratively (has no .nom property)');
39+
}
40+
// super prototype is either cached or we have to find it
41+
// by searching __proto__ (at the 'top')
42+
// invariant: because we cache _super on fn below, we never reach
43+
// here from inside a series of calls to super(), so it's ok to
44+
// start searching from the prototype of 'this' (at the 'top')
45+
// we must never memoize a null super for this reason
46+
_super = memoizeSuper(caller, nom, getPrototypeOf(this));
47+
}
48+
// our super function
49+
var fn = _super[nom];
50+
if (fn) {
51+
// memoize information so 'fn' can call 'super'
52+
if (!fn._super) {
53+
// must not memoize null, or we lose our invariant above
54+
memoizeSuper(fn, nom, _super);
55+
}
56+
// invoke the inherited method
57+
// if 'fn' is not function valued, this will throw
58+
return fn.apply(this, arrayOfArgs || []);
59+
}
60+
}
61+
62+
function nameInThis(value) {
63+
var p = this.__proto__;
64+
while (p && p !== HTMLElement.prototype) {
65+
// TODO(sjmiles): getOwnPropertyNames is absurdly expensive
66+
var n$ = Object.getOwnPropertyNames(p);
67+
for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
68+
var d = Object.getOwnPropertyDescriptor(p, n);
69+
if (typeof d.value === 'function' && d.value === value) {
70+
return n;
71+
}
72+
}
73+
p = p.__proto__;
74+
}
75+
}
76+
77+
function memoizeSuper(method, name, proto) {
78+
// find and cache next prototype containing `name`
79+
// we need the prototype so we can do another lookup
80+
// from here
81+
var s = nextSuper(proto, name, method);
82+
if (s[name]) {
83+
// `s` is a prototype, the actual method is `s[name]`
84+
// tag super method with it's name for quicker lookups
85+
s[name].nom = name;
86+
}
87+
return method._super = s;
88+
}
89+
90+
function nextSuper(proto, name, caller) {
91+
// look for an inherited prototype that implements name
92+
while (proto) {
93+
if ((proto[name] !== caller) && proto[name]) {
94+
return proto;
95+
}
96+
proto = getPrototypeOf(proto);
97+
}
98+
// must not return null, or we lose our invariant above
99+
// in this case, a super() call was invoked where no superclass
100+
// method exists
101+
// TODO(sjmiles): thow an exception?
102+
return Object;
103+
}
104+
105+
// NOTE: In some platforms (IE10) the prototype chain is faked via
106+
// __proto__. Therefore, always get prototype via __proto__ instead of
107+
// the more standard Object.getPrototypeOf.
108+
function getPrototypeOf(prototype) {
109+
return prototype.__proto__;
110+
}
111+
112+
// utility function to precompute name tags for functions
113+
// in a (unchained) prototype
114+
function hintSuper(prototype) {
115+
// tag functions with their prototype name to optimize
116+
// super call invocations
117+
for (var n in prototype) {
118+
var pd = Object.getOwnPropertyDescriptor(prototype, n);
119+
if (pd && typeof pd.value === 'function') {
120+
pd.value.nom = n;
121+
}
122+
}
123+
}
124+
125+
// exports
126+
127+
scope.super = $super;
128+
129+
})(Polymer);

0 commit comments

Comments
 (0)