mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2967 lines
80 KiB
2967 lines
80 KiB
/*
|
|
* Behavior of lightweight functions in various situations.
|
|
*
|
|
* Also documents the detailed behavior and limitations of lightfuncs.
|
|
* Specific limitations that matter have their own testcases (e.g.
|
|
* setter/getter limitations).
|
|
*
|
|
* Exhaustive built-in method tests don't all make practical sense (e.g.
|
|
* using a lightfunc as a 'this' binding for Array methods or giving a
|
|
* lightfunc as an argument to new Date()), but they document what
|
|
* currently happens and ensure there are no assertion failures or such.
|
|
* These checks are not 100%, e.g. if 'this' and arguments may be lightfuncs,
|
|
* giving two lightfuncs may cause a TypeError for 'this' validation and
|
|
* never ensure that the other argument is handled correctly.
|
|
*/
|
|
|
|
/*---
|
|
{
|
|
"custom": true
|
|
}
|
|
---*/
|
|
|
|
/* Some additional Function.prototype properties expected by tests below. */
|
|
try {
|
|
Object.defineProperties(Function.prototype, {
|
|
testWritable: { value: 123, writable: true, enumerable: false, configurable: false },
|
|
testNonWritable: { value: 234, writable: false, enumerable: false, configurable: false }
|
|
});
|
|
} catch (e) {
|
|
print(e.stack);
|
|
}
|
|
|
|
/* Shared object value which coerces to 'length' with a side effect.
|
|
* Used to verify string coercion and evaluation order.
|
|
*/
|
|
var objLengthKey = { toString: function () { print('toString coerced object (return "length")'); return "length"; } };
|
|
|
|
function getLightFunc() {
|
|
/* When using DUK_OPT_LIGHTFUNC_BUILTINS, all built-ins except just a
|
|
* handful are lightfuncs.
|
|
*
|
|
* Use math.max as a test function: it has a non-zero magic, it has
|
|
* length set to 2, but is still a varargs function.
|
|
*/
|
|
return Math.max;
|
|
}
|
|
|
|
function getNormalFunc() {
|
|
/* Even with DUK_OPT_LIGHTFUNC_BUILTINS, the top level constructors
|
|
* are not converted to lightfuncs: they have additional properties
|
|
* like Number.POSITIVE_INFINITY which would be lost.
|
|
*/
|
|
return Number;
|
|
}
|
|
|
|
function sanitizeLfunc(x) {
|
|
/* Escape Ecmascript comments which appear e.g. when coercing function
|
|
* values to string. Hide lightfunc pointer which is variable data.
|
|
*/
|
|
x = String(x);
|
|
x = x.replace(/\/\*/g, '(*').replace(/\*\//g, '*)');
|
|
x = x.replace(/light_[0-9a-fA-F]+_/g, 'light_PTR_', x);
|
|
x = x.replace(/LIGHT_[0-9a-fA-F]+_/g, 'LIGHT_PTR_', x);
|
|
return x;
|
|
}
|
|
|
|
function isLightFunc(x) {
|
|
return Duktape.info(x)[0] == 9; // tag
|
|
}
|
|
|
|
function printTypedJx(v, name) {
|
|
print(name + ':', typeof v, Duktape.enc('jx', v));
|
|
}
|
|
|
|
function testTypedJx(func, name) {
|
|
var res;
|
|
name = name || func.name || '???';
|
|
try {
|
|
res = func();
|
|
print(name + ':', typeof res, Duktape.enc('jx', res));
|
|
} catch (e) {
|
|
print(name + ':', e.name);
|
|
//print('*** ' + e);
|
|
}
|
|
}
|
|
|
|
function sanitizeTraceback(x) {
|
|
x = x.replace(/\/tmp\/.*?:/g, 'TESTCASE:');
|
|
x = x.replace(/:\d+/g, ':NNN')
|
|
x = x.replace(/\/\*/g, '(*').replace(/\*\//g, '*)');
|
|
x = x.replace(/light_[0-9a-fA-F]+_/g, 'light_PTR_', x);
|
|
x = x.replace(/LIGHT_[0-9a-fA-F]+_/g, 'LIGHT_PTR_', x);
|
|
return x;
|
|
}
|
|
|
|
/*===
|
|
light func support test
|
|
info.length: 1
|
|
typeof: function
|
|
[9]
|
|
===*/
|
|
|
|
function lightFuncSupportTest() {
|
|
/* Duktape.info() can be used to detect that light functions are
|
|
* supported.
|
|
*/
|
|
|
|
var fun = getLightFunc();
|
|
var info = Duktape.info(fun);
|
|
|
|
/* Value is primitive (no prop allocations etc) but still callable. */
|
|
print('info.length:', info.length);
|
|
print('typeof:', typeof fun);
|
|
print(Duktape.enc('jx', Duktape.info(fun)));
|
|
}
|
|
|
|
try {
|
|
print('light func support test');
|
|
lightFuncSupportTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
typeof test
|
|
function
|
|
===*/
|
|
|
|
function typeofTest() {
|
|
/* typeof of lightweight and ordinary functions should look the same
|
|
* for transparency, so: 'function'.
|
|
*/
|
|
|
|
var fun = getLightFunc();
|
|
|
|
print(typeof fun);
|
|
}
|
|
|
|
try {
|
|
print('typeof test');
|
|
typeofTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
property assignment test
|
|
strPlainNonStrict: success
|
|
strPlainStrict: TypeError
|
|
strObjectNonStrict: success
|
|
strObjectStrict: success
|
|
strObjectNonextNonStrict: success
|
|
strObjectNonextStrict: TypeError
|
|
lfuncNonStrict1: success
|
|
lfuncNonStrict2: success
|
|
lfuncStrict1: TypeError
|
|
lfuncStrict2: TypeError
|
|
===*/
|
|
|
|
function propertyAssignmentTest() {
|
|
/*
|
|
* Trying to assign a property value for a plain string value is a
|
|
* silent error in non-strict mode and TypeError in strict mode.
|
|
*/
|
|
|
|
var strPlainNonStrict = function () {
|
|
var str = 'foo';
|
|
str.foo = 'bar';
|
|
};
|
|
var strPlainStrict = function () {
|
|
'use strict';
|
|
var str = 'foo';
|
|
str.foo = 'bar';
|
|
};
|
|
try {
|
|
strPlainNonStrict();
|
|
print('strPlainNonStrict:', 'success');
|
|
} catch (e) {
|
|
print('strPlainNonStrict:', e.name);
|
|
}
|
|
|
|
try {
|
|
strPlainStrict();
|
|
print('strPlainStrict:', 'success');
|
|
} catch (e) {
|
|
print('strPlainStrict:', e.name);
|
|
}
|
|
|
|
/*
|
|
* Plain strings are not a good analogy for lightfunc behavior because
|
|
* lightfuncs try to behave like full objects. A closer analogy would
|
|
* be how a String object behaves.
|
|
*
|
|
* Here the property assignment succeeds because new String() returns
|
|
* an extensible object.
|
|
*/
|
|
|
|
var strObjectNonStrict = function () {
|
|
var str = new String('foo');
|
|
str.foo = 'bar';
|
|
};
|
|
var strObjectStrict = function () {
|
|
'use strict';
|
|
var str = new String('foo');
|
|
str.foo = 'bar';
|
|
};
|
|
try {
|
|
strObjectNonStrict();
|
|
print('strObjectNonStrict:', 'success');
|
|
} catch (e) {
|
|
print('strObjectNonStrict:', e.name);
|
|
}
|
|
|
|
try {
|
|
strObjectStrict();
|
|
print('strObjectStrict:', 'success');
|
|
} catch (e) {
|
|
print('strObjectStrict:', e.name);
|
|
}
|
|
|
|
/*
|
|
* Even closer analogy would be a new String() object which was made
|
|
* non-extensible.
|
|
*/
|
|
|
|
var strObjectNonextNonStrict = function () {
|
|
var str = new String('foo');
|
|
Object.preventExtensions(str);
|
|
str.foo = 'bar';
|
|
};
|
|
var strObjectNonextStrict = function () {
|
|
'use strict';
|
|
var str = new String('foo');
|
|
Object.preventExtensions(str);
|
|
str.foo = 'bar';
|
|
};
|
|
try {
|
|
strObjectNonextNonStrict();
|
|
print('strObjectNonextNonStrict:', 'success');
|
|
} catch (e) {
|
|
print('strObjectNonextNonStrict:', e.name);
|
|
}
|
|
|
|
try {
|
|
strObjectNonextStrict();
|
|
print('strObjectNonextStrict:', 'success');
|
|
} catch (e) {
|
|
print('strObjectNonextStrict:', e.name);
|
|
}
|
|
|
|
/*
|
|
* Because a lightfunc is not extensible and all own properties are
|
|
* non-configurable, the best behavior is silent success in non-strict
|
|
* mode (same happens e.g. when writing over 'length' of a String)
|
|
* and a TypeError in strict mode.
|
|
*
|
|
* This is now the behavior but error messages are not ideal. When
|
|
* the virtual property exists, the error is "not writable"; when it
|
|
* doesn't exist, the error is "invalid base value" ("not extensible"
|
|
* would be better).
|
|
*
|
|
* Another quite justifiable behavior would be to pretend as if the
|
|
* lightfunc was coerced to a full Function object before assignment.
|
|
* The coerced Function would now be extensible, so it would be possible
|
|
* to write new properties with no error. The current assignment behavior
|
|
* is not fully consistent with Object.defineProperty() behavior.
|
|
*/
|
|
|
|
var lfuncNonStrict1 = function () {
|
|
var lfunc = Math.max;
|
|
lfunc.name = 'foo';
|
|
}
|
|
var lfuncNonStrict2 = function () {
|
|
var lfunc = Math.max;
|
|
lfunc.nonexistent = 123;
|
|
}
|
|
var lfuncStrict1 = function () {
|
|
'use strict';
|
|
var lfunc = Math.max;
|
|
lfunc.name = 'foo';
|
|
}
|
|
var lfuncStrict2 = function () {
|
|
'use strict';
|
|
var lfunc = Math.max;
|
|
lfunc.nonexistent = 123;
|
|
}
|
|
|
|
try {
|
|
lfuncNonStrict1();
|
|
print('lfuncNonStrict1:', 'success');
|
|
} catch (e) {
|
|
print('lfuncNonStrict1:', e.name);
|
|
}
|
|
try {
|
|
lfuncNonStrict2();
|
|
print('lfuncNonStrict2:', 'success');
|
|
} catch (e) {
|
|
print('lfuncNonStrict2:', e.name);
|
|
}
|
|
try {
|
|
lfuncStrict1();
|
|
print('lfuncStrict1:', 'success');
|
|
} catch (e) {
|
|
print('lfuncStrict1:', e.name);
|
|
//print(e.stack);
|
|
}
|
|
try {
|
|
lfuncStrict2();
|
|
print('lfuncStrict2:', 'success');
|
|
} catch (e) {
|
|
print('lfuncStrict2:', e.name);
|
|
//print(e.stack);
|
|
}
|
|
}
|
|
|
|
try {
|
|
print('property assignment test');
|
|
propertyAssignmentTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
instanceof test
|
|
{} instanceof lightfunc: TypeError
|
|
false
|
|
{} instanceof func-wo-prototype: TypeError
|
|
lightFunc instanceof Function: true
|
|
lightFunc instanceof Number: false
|
|
lightFunc instanceof Object: true
|
|
===*/
|
|
|
|
function instanceofTest() {
|
|
var obj = {};
|
|
var lightFunc = getLightFunc();
|
|
var func;
|
|
|
|
/*
|
|
* When a lightweight function is on the right hand side, step 3
|
|
* of the algorithm in E5.1 Section 15.3.5.3 step will always
|
|
* throw a TypeError: lightweight functions don't have a virtual
|
|
* 'prototype' property.
|
|
*/
|
|
|
|
try {
|
|
print('{} instanceof lightfunc:', obj instanceof lightFunc);
|
|
} catch (e) {
|
|
print('{} instanceof lightfunc:', e.name);
|
|
}
|
|
|
|
/*
|
|
* The same happens for an ordinary function which doesn't have a
|
|
* 'prototype' property. To demonstrate this we need a function
|
|
* without a 'prototype' property: normal Ecmascript functions
|
|
* always have the property and it's not configurable so we can't
|
|
* delete it. Luckily many non-constructor built-ins don't have
|
|
* the property so we can use one of them.
|
|
*/
|
|
|
|
func = Math.cos;
|
|
print('prototype' in func);
|
|
try {
|
|
print('{} instanceof func-wo-prototype:', obj instanceof func);
|
|
} catch (e) {
|
|
print('{} instanceof func-wo-prototype:', e.name);
|
|
}
|
|
|
|
/*
|
|
* When a lightfunc appears on the left-hand side, the prototype
|
|
* walk begins with Function.prototype. Note that 'instanceof'
|
|
* never compares the original lhs value, it begins its walk from
|
|
* lhs's internal prototype.
|
|
*/
|
|
|
|
try {
|
|
print('lightFunc instanceof Function:', lightFunc instanceof Function);
|
|
} catch (e) {
|
|
print('lightFunc instanceof Function:', e.name);
|
|
}
|
|
|
|
try {
|
|
print('lightFunc instanceof Number:', lightFunc instanceof Number);
|
|
} catch (e) {
|
|
print('lightFunc instanceof Number:', e.name);
|
|
}
|
|
|
|
try {
|
|
print('lightFunc instanceof Object:', lightFunc instanceof Object);
|
|
} catch (e) {
|
|
print('lightFunc instanceof Object:', e.name);
|
|
}
|
|
}
|
|
|
|
try {
|
|
print('instanceof test');
|
|
instanceofTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
comparison test
|
|
0 0 true true
|
|
0 1 false false
|
|
0 2 false false
|
|
0 3 false false
|
|
0 4 false false
|
|
0 5 false false
|
|
0 6 false false
|
|
1 0 false false
|
|
1 1 true true
|
|
1 2 false false
|
|
1 3 false false
|
|
1 4 false false
|
|
1 5 false false
|
|
1 6 false false
|
|
2 0 false false
|
|
2 1 false false
|
|
2 2 true true
|
|
2 3 false false
|
|
2 4 false false
|
|
2 5 false false
|
|
2 6 false false
|
|
3 0 false false
|
|
3 1 false false
|
|
3 2 false false
|
|
3 3 true true
|
|
3 4 false false
|
|
3 5 false false
|
|
3 6 false false
|
|
4 0 false false
|
|
4 1 false false
|
|
4 2 false false
|
|
4 3 false false
|
|
4 4 true true
|
|
4 5 false false
|
|
4 6 false false
|
|
5 0 false false
|
|
5 1 false false
|
|
5 2 false false
|
|
5 3 false false
|
|
5 4 false false
|
|
5 5 true true
|
|
5 6 false false
|
|
6 0 false false
|
|
6 1 false false
|
|
6 2 false false
|
|
6 3 false false
|
|
6 4 false false
|
|
6 5 false false
|
|
6 6 true true
|
|
===*/
|
|
|
|
function comparisonTest() {
|
|
var lf1 = Math.cos;
|
|
var lf2 = Math.sin;
|
|
var lf3 = Array.prototype.forEach;
|
|
var nf1 = Object(lf1);
|
|
var nf2 = Object(lf2);
|
|
var nf3 = Object(lf3);
|
|
var ef1 = function ecmafunc() {};
|
|
var vals = [ lf1, lf2, lf3, nf1, nf2, nf3, ef1 ];
|
|
var i, j;
|
|
|
|
/*
|
|
* Comparison:
|
|
*
|
|
* - Lightfunc-to-lightfunc comparison compares both Duktape/C
|
|
* function pointers and the flags value (magic + other flags).
|
|
* Because the same Duktape/C function is often shared for very
|
|
* different functions, it's important to include at least magic
|
|
* in the comparison.
|
|
*
|
|
* - Lightfunc never compares equal to an ordinary Function, even
|
|
* when the Function was created as ToObject(lightFunc). This
|
|
* mimics the general behavior for Function objects, which always
|
|
* compare strictly by reference.
|
|
*/
|
|
|
|
// XXX: samevalue
|
|
|
|
for (i = 0; i < vals.length; i++) {
|
|
for (j = 0; j < vals.length; j++) {
|
|
print(i, j, vals[i] == vals[j], vals[i] === vals[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
print('comparison test');
|
|
comparisonTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
arithmetic test
|
|
string: testfunction light_PTR_0511() {(* light *)}function light_PTR_0a11() {(* light *)}
|
|
string: function light_PTR_0511() {(* light *)}function light_PTR_0a11() {(* light *)}
|
|
string: function foo() {(* ecmascript *)}function bar() {(* ecmascript *)}
|
|
===*/
|
|
|
|
function arithmeticTest() {
|
|
function p(x) {
|
|
print(typeof x + ': ' + sanitizeLfunc(x));
|
|
}
|
|
|
|
p('test' + Math.cos + Math.sin);
|
|
p(Math.cos + Math.sin);
|
|
p((function foo(){}) + (function bar(){}));
|
|
}
|
|
|
|
try {
|
|
print('arithmetic test');
|
|
arithmeticTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
toString() test
|
|
function light_PTR_002f() {(* light *)}
|
|
function light_PTR_002f() {(* light *)}
|
|
function light_PTR_002f() {(* light *)}
|
|
true
|
|
true
|
|
===*/
|
|
|
|
function toStringTest() {
|
|
/* String coercion of functions is not strictly defined - so here the
|
|
* coercion output can identify the function as a lightweight function.
|
|
*
|
|
* Because the string coercion output includes Ecmascript comment chars
|
|
* and a variable pointer, we need sanitization before printing.
|
|
*/
|
|
|
|
var fun = getLightFunc();
|
|
|
|
print(sanitizeLfunc(String(fun)));
|
|
print(sanitizeLfunc(fun.toString()));
|
|
print(sanitizeLfunc(Function.prototype.toString.call(fun)));
|
|
|
|
/* ToString(fun) and Function.prototype.toString(fun) should match for
|
|
* lightfuncs.
|
|
*/
|
|
print(String(fun) === fun.toString());
|
|
print(String(fun) === Function.prototype.toString.call(fun));
|
|
}
|
|
|
|
try {
|
|
print('toString() test');
|
|
toStringTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
toObject() test
|
|
caching: false
|
|
length: 2 2
|
|
name: light_PTR_002f light_PTR_002f
|
|
typeof: function function
|
|
internal prototype is Function.prototype: true true
|
|
external prototype is not set: true
|
|
internal prototypes match: true
|
|
external prototypes match (do not exist): true
|
|
isExtensible: false true
|
|
Math.max test: 9 9
|
|
length: 1 1
|
|
===*/
|
|
|
|
function toObjectTest() {
|
|
/* Object coercion creates a normal Duktape/C function object. A few
|
|
* things to watch out for:
|
|
*
|
|
* - varargs vs. fixed args
|
|
*
|
|
* The fact that the virtual lightfunc name is copied over to the
|
|
* non-light function is somewhat misleading, but still maybe the
|
|
* best option?
|
|
*/
|
|
|
|
var lightFunc = Math.max; // Math.max has 'length' 2, but is varargs
|
|
var normalFunc = Object(lightFunc);
|
|
|
|
// Object coercion always results in a new object, there is no "caching"
|
|
print('caching:', Object(lightFunc) === Object(lightFunc));
|
|
|
|
print('length:', lightFunc.length, normalFunc.length);
|
|
print('name:', sanitizeLfunc(lightFunc.name), sanitizeLfunc(normalFunc.name));
|
|
print('typeof:', typeof lightFunc, typeof normalFunc);
|
|
print('internal prototype is Function.prototype:',
|
|
Object.getPrototypeOf(lightFunc) === Function.prototype,
|
|
Object.getPrototypeOf(normalFunc) === Function.prototype);
|
|
print('external prototype is not set:',
|
|
lightFunc.prototype === undefined);
|
|
print('internal prototypes match:',
|
|
Object.getPrototypeOf(lightFunc) === Object.getPrototypeOf(normalFunc));
|
|
print('external prototypes match (do not exist):',
|
|
lightFunc.prototype === normalFunc.prototype);
|
|
|
|
// Although a lightfunc is not extensible, the coercion result is to
|
|
// match behavior for e.g. strings:
|
|
//
|
|
// > Object.isExtensible(Object('foo'))
|
|
// true
|
|
print('isExtensible:', Object.isExtensible(lightFunc), Object.isExtensible(normalFunc));
|
|
|
|
// Here the max value (9) should be further than what the apparent
|
|
// 'length' is
|
|
print('Math.max test:', lightFunc(1, 5, 7, 4, 9, 2), normalFunc(1, 5, 7, 4, 9, 2));
|
|
|
|
// Math.cos has 'length' 1 and is not varargs
|
|
lightFunc = Math.cos;
|
|
normalFunc = Object(lightFunc);
|
|
print('length:', lightFunc.length, normalFunc.length);
|
|
|
|
// XXX: other properties
|
|
}
|
|
|
|
try {
|
|
print('toObject() test');
|
|
toObjectTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
toBoolean() test
|
|
true
|
|
true
|
|
===*/
|
|
|
|
function toBooleanTest() {
|
|
var lfunc = Math.cos;
|
|
var nfunc = function foo() {};
|
|
|
|
print(Boolean(lfunc));
|
|
print(Boolean(nfunc));
|
|
}
|
|
|
|
try {
|
|
print('toBoolean() test');
|
|
toBooleanTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
toBuffer() test
|
|
buffer: function light_PTR_0511() {(* light *)}
|
|
buffer: function light_PTR_0a11() {(* light *)}
|
|
===*/
|
|
|
|
function toBufferTest() {
|
|
/* Duktape.Buffer(v) does -not- implement the same semantics as the
|
|
* ToBuffer() coercion provided by duk_to_buffer() API call. The API
|
|
* ToBuffer() is not directly available, but Duktape.enc('base64', ...)
|
|
* will (currently) first call a duk_to_buffer() on the argument so we
|
|
* can use that to get at ToBuffer().
|
|
*/
|
|
|
|
function tobuf(x) {
|
|
return Duktape.dec('base64', Duktape.enc('base64', x));
|
|
}
|
|
function printbuf(x) {
|
|
var tmp = [];
|
|
var i;
|
|
tmp.push(typeof x + ': '); // avoid printing length, depends on ptr length
|
|
for (i = 0; i < x.length; i++) {
|
|
if (x[i] >= 0x20 && x[i] <= 0x7e) {
|
|
tmp.push(String.fromCharCode(x[i]));
|
|
} else {
|
|
tmp.push('<' + Number(x[i]).toString(16) + '>');
|
|
}
|
|
}
|
|
print(sanitizeLfunc(tmp.join('')));
|
|
}
|
|
|
|
printbuf(tobuf(Math.cos));
|
|
printbuf(tobuf(Math.sin));
|
|
}
|
|
|
|
try {
|
|
print('toBuffer() test');
|
|
toBufferTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
toPointer() test
|
|
pointer null
|
|
object null
|
|
===*/
|
|
|
|
function toPointerTest() {
|
|
var lfunc = Math.cos;
|
|
var t;
|
|
|
|
t = Duktape.Pointer(lfunc);
|
|
print(typeof t, t);
|
|
|
|
t = new Duktape.Pointer(lfunc);
|
|
print(typeof t, t);
|
|
}
|
|
|
|
try {
|
|
print('toPointer() test');
|
|
toPointerTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
number coercion test
|
|
NaN
|
|
NaN
|
|
0
|
|
0
|
|
0
|
|
0
|
|
0
|
|
0
|
|
===*/
|
|
|
|
function numberCoercionTest() {
|
|
var lfunc = Math.cos;
|
|
var nfunc = function foo(){};
|
|
|
|
// ToNumber(): NaN
|
|
print(Number(lfunc));
|
|
print(Number(nfunc));
|
|
|
|
// ToInteger(): positive zero
|
|
// XXX: no test, where to get ToInteger() result most easily?
|
|
|
|
// ToInt32(), ToUint32(), ToUint16(): positive zero
|
|
print(lfunc >>> 0); // ToUint32
|
|
print(nfunc >>> 0);
|
|
print(lfunc >> 0); // ToInt32
|
|
print(nfunc >> 0);
|
|
print(String.fromCharCode(lfunc).charCodeAt(0)); // ToUint16
|
|
print(String.fromCharCode(nfunc).charCodeAt(0));
|
|
|
|
}
|
|
|
|
try {
|
|
print('number coercion test');
|
|
numberCoercionTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
call and apply test
|
|
call
|
|
321
|
|
apply
|
|
987
|
|
===*/
|
|
|
|
function callApplyTest() {
|
|
/* Lightfuncs inherit from Function.prototype (similarly to how plain
|
|
* strings inherit from String.prototype). This means you can use
|
|
* call() and apply().
|
|
*/
|
|
|
|
var fun = Math.max; // length 2, varargs
|
|
|
|
print('call');
|
|
print(fun.call('myThis', 123, 321));
|
|
|
|
print('apply');
|
|
print(fun.apply('myThis', [ 123, 321, 987, 345 ]));
|
|
}
|
|
|
|
try {
|
|
print('call and apply test');
|
|
callApplyTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
this coercion test
|
|
function true
|
|
function true
|
|
===*/
|
|
|
|
function thisCoercionTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
// Strict functions get 'this' as is as normal, including lightfuncs.
|
|
|
|
function myStrict() {
|
|
'use strict';
|
|
print(typeof this, isLightFunc(this));
|
|
}
|
|
myStrict.call(lfunc);
|
|
|
|
// The 'this' binding of a non-strict function is not further coerced if
|
|
// it is an object. There are two logical behaviors here for lightfuncs:
|
|
// either we (1) treat them like objects and don't coerce them; or (2)
|
|
// coerce them forcibly to a fully fledged object.
|
|
//
|
|
// Current behavior is (1) so the 'this' binding should also be lightfunc
|
|
// in myNonStrict.
|
|
|
|
function myNonStrict() {
|
|
print(typeof this, isLightFunc(this));
|
|
}
|
|
myNonStrict.call(lfunc);
|
|
}
|
|
|
|
try {
|
|
print('this coercion test');
|
|
thisCoercionTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
inherit from Function.prototype test
|
|
testValue
|
|
===*/
|
|
|
|
function inheritFromFunctionPrototypeTest() {
|
|
var fun = getLightFunc();
|
|
|
|
Function.prototype.inheritTestProperty = 'testValue';
|
|
print(fun.inheritTestProperty);
|
|
}
|
|
|
|
try {
|
|
print('inherit from Function.prototype test');
|
|
inheritFromFunctionPrototypeTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Object.prototype.toString() test
|
|
[object Function]
|
|
===*/
|
|
|
|
function objectPrototypeToStringTest() {
|
|
/* Object.prototype.toString() for a light function should look same as
|
|
* for actual functions to make them as transparent as possible, so:
|
|
* "[object Function]".
|
|
*/
|
|
|
|
var fun = getLightFunc();
|
|
|
|
print(Object.prototype.toString.call(fun));
|
|
}
|
|
|
|
try {
|
|
print('Object.prototype.toString() test');
|
|
objectPrototypeToStringTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
JSON/JX/JC test
|
|
json
|
|
undefined
|
|
undefined
|
|
jx
|
|
{_func:true}
|
|
{_func:true}
|
|
jc
|
|
{"_func":true}
|
|
{"_func":true}
|
|
json
|
|
undefined
|
|
undefined
|
|
jx
|
|
{_func:true}
|
|
{_func:true}
|
|
jc
|
|
{"_func":true}
|
|
{"_func":true}
|
|
json
|
|
{"array":[100,null,200,null,300]}
|
|
{
|
|
"array": [
|
|
100,
|
|
null,
|
|
200,
|
|
null,
|
|
300
|
|
]
|
|
}
|
|
jx
|
|
{lf:{_func:true},nf:{_func:true},array:[100,{_func:true},200,{_func:true},300]}
|
|
{
|
|
lf: {_func:true},
|
|
nf: {_func:true},
|
|
array: [
|
|
100,
|
|
{_func:true},
|
|
200,
|
|
{_func:true},
|
|
300
|
|
]
|
|
}
|
|
jc
|
|
{"lf":{"_func":true},"nf":{"_func":true},"array":[100,{"_func":true},200,{"_func":true},300]}
|
|
{
|
|
"lf": {"_func":true},
|
|
"nf": {"_func":true},
|
|
"array": [
|
|
100,
|
|
{"_func":true},
|
|
200,
|
|
{"_func":true},
|
|
300
|
|
]
|
|
}
|
|
json
|
|
"toJsonRetval"
|
|
"toJsonRetval"
|
|
jx
|
|
"toJsonRetval"
|
|
"toJsonRetval"
|
|
jc
|
|
"toJsonRetval"
|
|
"toJsonRetval"
|
|
json
|
|
"toJsonRetval"
|
|
"toJsonRetval"
|
|
jx
|
|
"toJsonRetval"
|
|
"toJsonRetval"
|
|
jc
|
|
"toJsonRetval"
|
|
"toJsonRetval"
|
|
json
|
|
{"lf":"toJsonRetval","nf":"toJsonRetval","array":[100,"toJsonRetval",200,"toJsonRetval",300]}
|
|
{
|
|
"lf": "toJsonRetval",
|
|
"nf": "toJsonRetval",
|
|
"array": [
|
|
100,
|
|
"toJsonRetval",
|
|
200,
|
|
"toJsonRetval",
|
|
300
|
|
]
|
|
}
|
|
jx
|
|
{lf:"toJsonRetval",nf:"toJsonRetval",array:[100,"toJsonRetval",200,"toJsonRetval",300]}
|
|
{
|
|
lf: "toJsonRetval",
|
|
nf: "toJsonRetval",
|
|
array: [
|
|
100,
|
|
"toJsonRetval",
|
|
200,
|
|
"toJsonRetval",
|
|
300
|
|
]
|
|
}
|
|
jc
|
|
{"lf":"toJsonRetval","nf":"toJsonRetval","array":[100,"toJsonRetval",200,"toJsonRetval",300]}
|
|
{
|
|
"lf": "toJsonRetval",
|
|
"nf": "toJsonRetval",
|
|
"array": [
|
|
100,
|
|
"toJsonRetval",
|
|
200,
|
|
"toJsonRetval",
|
|
300
|
|
]
|
|
}
|
|
json
|
|
0
|
|
0
|
|
jx
|
|
0
|
|
0
|
|
jc
|
|
0
|
|
0
|
|
json
|
|
0
|
|
0
|
|
jx
|
|
0
|
|
0
|
|
jc
|
|
0
|
|
0
|
|
json
|
|
{"lf":null,"nf":null,"array":[100,1,200,3,300]}
|
|
{
|
|
"lf": null,
|
|
"nf": null,
|
|
"array": [
|
|
100,
|
|
1,
|
|
200,
|
|
3,
|
|
300
|
|
]
|
|
}
|
|
jx
|
|
{lf:NaN,nf:NaN,array:[100,1,200,3,300]}
|
|
{
|
|
lf: NaN,
|
|
nf: NaN,
|
|
array: [
|
|
100,
|
|
1,
|
|
200,
|
|
3,
|
|
300
|
|
]
|
|
}
|
|
jc
|
|
{"lf":{"_nan":true},"nf":{"_nan":true},"array":[100,1,200,3,300]}
|
|
{
|
|
"lf": {"_nan":true},
|
|
"nf": {"_nan":true},
|
|
"array": [
|
|
100,
|
|
1,
|
|
200,
|
|
3,
|
|
300
|
|
]
|
|
}
|
|
===*/
|
|
|
|
function jsonJxJcTest() {
|
|
/* There's a separate test for this too, but lightweight functions look
|
|
* like functions in JSON/JX/JC output.
|
|
*/
|
|
|
|
var lightFunc = getLightFunc();
|
|
var normalFunc = getNormalFunc();
|
|
|
|
var testValue1 = lightFunc;
|
|
var testValue2 = normalFunc;
|
|
var testValue3 = {
|
|
lf: lightFunc,
|
|
nf: normalFunc,
|
|
array: [ 100, lightFunc, 200, normalFunc, 300 ]
|
|
};
|
|
|
|
[ testValue1, testValue2, testValue3 ].forEach(function (v) {
|
|
print('json');
|
|
print(JSON.stringify(v));
|
|
print(JSON.stringify(v, null, 4));
|
|
print('jx');
|
|
print(Duktape.enc('jx', v));
|
|
print(Duktape.enc('jx', v, null, 4));
|
|
print('jc');
|
|
print(Duktape.enc('jc', v));
|
|
print(Duktape.enc('jc', v, null, 4));
|
|
});
|
|
|
|
/* toJSON() should work, and is inherited from Function.prototype.
|
|
* XXX: right now the 'this' binding will be a lightfunc coerced
|
|
* to a normal function, so 'toJsonRetval' is returned.
|
|
*/
|
|
|
|
Function.prototype.toJSON = function (key) {
|
|
//print('toJSON, this-is-lightfunc:', isLightFunc(this), 'key:', key);
|
|
if (isLightFunc(this)) {
|
|
return 'toJsonLightfuncRetval';
|
|
}
|
|
return 'toJsonRetval';
|
|
};
|
|
|
|
[ testValue1, testValue2, testValue3 ].forEach(function (v) {
|
|
print('json');
|
|
print(JSON.stringify(v));
|
|
print(JSON.stringify(v, null, 4));
|
|
print('jx');
|
|
print(Duktape.enc('jx', v));
|
|
print(Duktape.enc('jx', v, null, 4));
|
|
print('jc');
|
|
print(Duktape.enc('jc', v));
|
|
print(Duktape.enc('jc', v, null, 4));
|
|
});
|
|
|
|
delete Function.prototype.toJSON;
|
|
|
|
/* The toJSON function itself can also be a lightfunc. Below we use
|
|
* Math.min() as the toJSON() function: it will return:
|
|
* - NaN when the key is a non-empty string
|
|
* - 0 when the key is an empty string (the top level 'holder' key
|
|
* is an empty string)
|
|
* - key as is, if the key is a number (array case)
|
|
*/
|
|
|
|
Function.prototype.toJSON = Math.min;
|
|
|
|
[ testValue1, testValue2, testValue3 ].forEach(function (v) {
|
|
print('json');
|
|
print(JSON.stringify(v));
|
|
print(JSON.stringify(v, null, 4));
|
|
print('jx');
|
|
print(Duktape.enc('jx', v));
|
|
print(Duktape.enc('jx', v, null, 4));
|
|
print('jc');
|
|
print(Duktape.enc('jc', v));
|
|
print(Duktape.enc('jc', v, null, 4));
|
|
});
|
|
|
|
delete Function.prototype.toJSON;
|
|
}
|
|
|
|
try {
|
|
print('JSON/JX/JC test');
|
|
jsonJxJcTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
bound function test
|
|
F: function light_PTR_002f() {(* light *)}
|
|
F type tag: 9
|
|
G: function light_PTR_002f() {(* bound *)}
|
|
G type tag: 6
|
|
G.length: 1
|
|
H: function light_PTR_002f() {(* bound *)}
|
|
H type tag: 6
|
|
H.length: 0
|
|
I: function light_PTR_002f() {(* bound *)}
|
|
I type tag: 6
|
|
I.length: 0
|
|
G(123): 234
|
|
G(123,987): 987
|
|
===*/
|
|
|
|
function boundFunctionTest() {
|
|
/* A lightweight function can be bound normally. The 'length' property
|
|
* must be correctly copied from the virtual 'length' of the lightfunc.
|
|
*/
|
|
|
|
var F = Math.max; // length 2, varargs
|
|
print('F:', sanitizeLfunc(String(F)));
|
|
print('F type tag:', Duktape.info(F)[0]);
|
|
|
|
// 'G' will be a full Function object but will still have a lightfunc
|
|
// 'name' as a result of ToObject coercion, which is intentional but
|
|
// somewhat confusing. This would be fixable in the ToObject() coercion.
|
|
var G = F.bind('myThis', 234); // bound once
|
|
print('G:', sanitizeLfunc(String(G)));
|
|
print('G type tag:', Duktape.info(G)[0]);
|
|
print('G.length:', G.length); // length 1, one argument was bound
|
|
|
|
var H = G.bind('foo', 345); // bound twice
|
|
print('H:', sanitizeLfunc(String(H)));
|
|
print('H type tag:', Duktape.info(H)[0]);
|
|
print('H.length:', H.length); // length 0, two arguments are bound
|
|
|
|
var I = H.bind('foo', 345); // bound three times
|
|
print('I:', sanitizeLfunc(String(I)));
|
|
print('I type tag:', Duktape.info(I)[0]);
|
|
print('I.length:', I.length); // length 0, doesn't become negative
|
|
|
|
// Another simple test
|
|
print('G(123):', G(123));
|
|
print('G(123,987):', G(123, 987));
|
|
}
|
|
|
|
try {
|
|
print('bound function test');
|
|
boundFunctionTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
property get test
|
|
length directly: 2
|
|
toString coerced object (return "length")
|
|
objLengthKey coerced to string: length
|
|
toString coerced object (return "length")
|
|
length through object coercion: 2
|
|
read from length -> 2
|
|
read from prototype -> undefined
|
|
read from name -> light_PTR_002f
|
|
toString coerced object (return "length")
|
|
toString coerced object (return "length")
|
|
read from length -> 2
|
|
read from testWritable -> 123
|
|
read from testNonWritable -> 234
|
|
read from call -> function light_PTR_001f() {(* light *)}
|
|
read from apply -> function light_PTR_0022() {(* light *)}
|
|
read from nonexistent -> undefined
|
|
===*/
|
|
|
|
function propertyGetTest() {
|
|
var lightFunc = getLightFunc();
|
|
|
|
/*
|
|
* Property get is relatively simple. Virtual properties are matched
|
|
* first, and then we continue with Function.prototype for lookup.
|
|
*
|
|
* An inherited getter is a special case.
|
|
*
|
|
* NOTE: duk_hobject_props.c must string coerce the key before comparison
|
|
* because an object may coerce to a virtual key name
|
|
*/
|
|
|
|
print('length directly:', lightFunc.length);
|
|
print('objLengthKey coerced to string:', String(objLengthKey));
|
|
print('length through object coercion:', lightFunc[objLengthKey]);
|
|
|
|
var testKeys = [
|
|
'length', 'prototype', 'name', // own properties
|
|
objLengthKey, // own, object coerces to 'length'
|
|
'testWritable', // inherited, writable
|
|
'testNonWritable', // inherited, non-writable
|
|
'call', 'apply', // inherited, standard built-in
|
|
'nonexistent' // non-existent
|
|
];
|
|
|
|
testKeys.forEach(function (k) {
|
|
try {
|
|
print('read from', k, '->', sanitizeLfunc(lightFunc[k]));
|
|
} catch (e) {
|
|
print('read from', k, '->', e.name);
|
|
}
|
|
});
|
|
}
|
|
|
|
try {
|
|
print('property get test');
|
|
propertyGetTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
property put test
|
|
write to length -> silent error
|
|
write to prototype -> silent error
|
|
write to name -> silent error
|
|
toString coerced object (return "length")
|
|
toString coerced object (return "length")
|
|
write to length -> silent error
|
|
write to testWritable -> silent error
|
|
write to testNonWritable -> silent error
|
|
write to call -> silent error
|
|
write to apply -> silent error
|
|
write to nonexistent -> silent error
|
|
write to length -> TypeError
|
|
write to prototype -> TypeError
|
|
write to name -> TypeError
|
|
toString coerced object (return "length")
|
|
toString coerced object (return "length")
|
|
write to length -> TypeError
|
|
write to testWritable -> TypeError
|
|
write to testNonWritable -> TypeError
|
|
write to call -> TypeError
|
|
write to apply -> TypeError
|
|
write to nonexistent -> TypeError
|
|
===*/
|
|
|
|
function propertyPutTest() {
|
|
var lightFunc = getLightFunc();
|
|
|
|
/*
|
|
* The own properties of a lightfunc are not writable. It is also not
|
|
* extensible (having no place to write properties), so property writes
|
|
* fail with TypeError in almost every case:
|
|
*
|
|
* - Own (virtual) property: not writable
|
|
* - Inherited property, non-writable: not writable
|
|
* - Inherited property, writable: not extensible
|
|
* - Non-existent: not extensible
|
|
* - Inherited property, setter: *setter must be called*
|
|
*
|
|
* The setter case is handled by a separate test function.
|
|
*/
|
|
|
|
var testKeys = [
|
|
'length', 'prototype', 'name', // own properties
|
|
objLengthKey, // own, object coerces to 'length'
|
|
'testWritable', // inherited, writable
|
|
'testNonWritable', // inherited, non-writable
|
|
'call', 'apply', // inherited, standard built-in
|
|
'nonexistent' // non-existent
|
|
];
|
|
|
|
testKeys.forEach(function (k) {
|
|
// non-strict, errors are ignored
|
|
try {
|
|
lightFunc[k] = 'test';
|
|
print('write to', k, '->', 'silent error');
|
|
} catch (e) {
|
|
print('write to', k, '->', e.name);
|
|
}
|
|
});
|
|
|
|
testKeys.forEach(function (k) {
|
|
'use strict'; // must be strict to cause throwing
|
|
try {
|
|
lightFunc[k] = 'test';
|
|
print('never here:', k);
|
|
} catch (e) {
|
|
print('write to', k, '->', e.name);
|
|
}
|
|
});
|
|
}
|
|
|
|
try {
|
|
print('property put test');
|
|
propertyPutTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
property has test
|
|
existence: length -> true
|
|
existence: prototype -> false
|
|
existence: name -> true
|
|
toString coerced object (return "length")
|
|
toString coerced object (return "length")
|
|
existence: length -> true
|
|
existence: testWritable -> true
|
|
existence: testNonWritable -> true
|
|
existence: call -> true
|
|
existence: apply -> true
|
|
existence: nonexistent -> false
|
|
===*/
|
|
|
|
function propertyHasTest() {
|
|
var lightFunc = getLightFunc();
|
|
|
|
/*
|
|
* Property existence test must account for own (virtual) properties
|
|
* and inherited properties.
|
|
*/
|
|
|
|
var testKeys = [
|
|
'length', 'prototype', 'name', // own
|
|
objLengthKey, // own, object coerces to 'length'
|
|
'testWritable', // inherited
|
|
'testNonWritable', // inherited
|
|
'call', 'apply', // inherited
|
|
'nonexistent' // non-existent
|
|
];
|
|
|
|
testKeys.forEach(function (k) {
|
|
print('existence:', k, '->', k in lightFunc);
|
|
});
|
|
}
|
|
|
|
try {
|
|
print('property has test');
|
|
propertyHasTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
property delete test
|
|
delete: length -> false
|
|
delete: prototype -> true
|
|
delete: name -> false
|
|
toString coerced object (return "length")
|
|
toString coerced object (return "length")
|
|
delete: length -> false
|
|
delete: testWritable -> true
|
|
delete: testNonWritable -> true
|
|
delete: call -> true
|
|
delete: apply -> true
|
|
delete: nonexistent -> true
|
|
delete: length -> TypeError
|
|
delete: prototype -> true
|
|
delete: name -> TypeError
|
|
toString coerced object (return "length")
|
|
toString coerced object (return "length")
|
|
delete: length -> TypeError
|
|
delete: testWritable -> true
|
|
delete: testNonWritable -> true
|
|
delete: call -> true
|
|
delete: apply -> true
|
|
delete: nonexistent -> true
|
|
non-strict: true
|
|
strict: true
|
|
===*/
|
|
|
|
function propertyDeleteTest() {
|
|
var lightFunc = getLightFunc();
|
|
|
|
/*
|
|
* Property deletions only affect own properties. Since all lightfunc
|
|
* virtual properties are non-configurable:
|
|
*
|
|
* - Own (virtual) property: not configurable
|
|
* - Non-existent property: silent success
|
|
*/
|
|
|
|
// existence for own properties and a few inherited
|
|
var testKeys = [
|
|
'length', 'prototype', 'name', // own
|
|
objLengthKey, // own, object coerces to 'length'
|
|
'testWritable', // inherited
|
|
'testNonWritable', // inherited
|
|
'call', 'apply', // inherited
|
|
'nonexistent' // non-existent
|
|
];
|
|
|
|
testKeys.forEach(function (k) {
|
|
// non-strict, errors return false but don't throw
|
|
try {
|
|
print('delete:', k, '->', delete lightFunc[k]);
|
|
} catch (e) {
|
|
print('delete:', k, '->', e.name);
|
|
}
|
|
});
|
|
|
|
testKeys.forEach(function (k) {
|
|
'use strict'; // promote to TypeError
|
|
try {
|
|
print('delete:', k, '->', delete lightFunc[k]);
|
|
} catch (e) {
|
|
print('delete:', k, '->', e.name);
|
|
}
|
|
});
|
|
|
|
// Deletion of a non-existent property is a silent success in both
|
|
// strict and non-strict mode. Below this is demonstrated also for
|
|
// normal functions.
|
|
|
|
var func = function () {};
|
|
try {
|
|
(function () { print('non-strict:', delete func.nonexistent); })();
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
try {
|
|
(function () { 'use strict'; print('strict:', delete func.nonexistent); })();
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
}
|
|
|
|
try {
|
|
print('property delete test');
|
|
propertyDeleteTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
property accessor this binding test
|
|
getter, strict
|
|
strict getter "this" binding test
|
|
typeof this: function
|
|
this == lightFunc: true
|
|
this === lightFunc: true
|
|
this.name: light_PTR_002f
|
|
type tag: 9
|
|
getter retval
|
|
setter, strict
|
|
strict setter "this" binding test
|
|
typeof this: function
|
|
this == lightFunc: true
|
|
this === lightFunc: true
|
|
this.name: light_PTR_002f
|
|
type tag: 9
|
|
getter, non-strict
|
|
non-strict getter "this" binding test
|
|
typeof this: function
|
|
this == lightFunc: true
|
|
this === lightFunc: true
|
|
this.name: light_PTR_002f
|
|
type tag: 9
|
|
getter retval
|
|
setter, non-strict
|
|
non-strict setter "this" binding test
|
|
typeof this: function
|
|
this == lightFunc: true
|
|
this === lightFunc: true
|
|
this.name: light_PTR_002f
|
|
type tag: 9
|
|
===*/
|
|
|
|
function propertyAccessorThisBindingTest() {
|
|
var lightFunc = getLightFunc();
|
|
|
|
/*
|
|
* If a getter/setter is triggered by reading/writing an inherited
|
|
* property, the getter/setter 'this' binding is set to the lightfunc
|
|
* (and not, e.g., Function.prototype).
|
|
*
|
|
* Because a lightfunc behaves like a full Function object, it is
|
|
* not coerced with ToObject() even when the accessor is non-strict.
|
|
*/
|
|
|
|
Object.defineProperty(Function.prototype, 'testAccessorStrict', {
|
|
get: function () {
|
|
'use strict';
|
|
print('strict getter "this" binding test');
|
|
print('typeof this:', typeof this);
|
|
print('this == lightFunc:', this == lightFunc);
|
|
print('this === lightFunc:', this === lightFunc);
|
|
print('this.name:', sanitizeLfunc(this.name));
|
|
print('type tag:', Duktape.info(this)[0]);
|
|
return 'getter retval';
|
|
},
|
|
set: function () {
|
|
'use strict';
|
|
print('strict setter "this" binding test');
|
|
print('typeof this:', typeof this);
|
|
print('this == lightFunc:', this == lightFunc);
|
|
print('this === lightFunc:', this === lightFunc);
|
|
print('this.name:', sanitizeLfunc(this.name));
|
|
print('type tag:', Duktape.info(this)[0]);
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
|
|
Object.defineProperty(Function.prototype, 'testAccessorNonStrict', {
|
|
get: function () {
|
|
print('non-strict getter "this" binding test');
|
|
print('typeof this:', typeof this);
|
|
print('this == lightFunc:', this == lightFunc);
|
|
print('this === lightFunc:', this === lightFunc);
|
|
print('this.name:', sanitizeLfunc(this.name));
|
|
print('type tag:', Duktape.info(this)[0]);
|
|
return 'getter retval';
|
|
},
|
|
set: function () {
|
|
print('non-strict setter "this" binding test');
|
|
print('typeof this:', typeof this);
|
|
print('this == lightFunc:', this == lightFunc);
|
|
print('this === lightFunc:', this === lightFunc);
|
|
print('this.name:', sanitizeLfunc(this.name));
|
|
print('type tag:', Duktape.info(this)[0]);
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
|
|
print('getter, strict');
|
|
print(lightFunc.testAccessorStrict);
|
|
print('setter, strict');
|
|
lightFunc.testAccessorStrict = 123;
|
|
|
|
print('getter, non-strict');
|
|
print(lightFunc.testAccessorNonStrict);
|
|
print('setter, non-strict');
|
|
lightFunc.testAccessorNonStrict = 123;
|
|
}
|
|
|
|
try {
|
|
print('property accessor this binding test');
|
|
propertyAccessorThisBindingTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
property misc test
|
|
isSealed: true
|
|
isFrozen: true
|
|
isExtensible: false
|
|
for-in: "inheritTestProperty"
|
|
Object.getOwnPropertyNames: "length"
|
|
Object.getOwnPropertyNames: "name"
|
|
lightFunc.name matches regexp: true
|
|
censored lightFunc.name: light_XXX_XXX
|
|
===*/
|
|
|
|
function propertyMiscTest() {
|
|
var lightFunc = getLightFunc();
|
|
var t, k;
|
|
|
|
/*
|
|
* A lightfunc is considered sealed, frozen, and non-extensible.
|
|
*/
|
|
|
|
print('isSealed:', Object.isSealed(lightFunc));
|
|
print('isFrozen:', Object.isFrozen(lightFunc));
|
|
print('isExtensible:', Object.isExtensible(lightFunc));
|
|
|
|
/*
|
|
* Enumeration: enumerable keys (both own and inherited). Because all
|
|
* own properties are non-enumerable, only enumerable inherited keys
|
|
* print out here.
|
|
*/
|
|
|
|
for (k in lightFunc) {
|
|
print('for-in:', JSON.stringify(k));
|
|
}
|
|
|
|
/*
|
|
* Object.keys(): only returns own non-enumerable keys, so -nothing-
|
|
* prints out here.
|
|
*/
|
|
|
|
Object.keys(lightFunc).forEach(function (k) {
|
|
print('Object.keys:', JSON.stringify(k));
|
|
});
|
|
|
|
/*
|
|
* Own property names: own property names (virtual properties) print out.
|
|
*/
|
|
|
|
Object.getOwnPropertyNames(lightFunc).forEach(function (k) {
|
|
print('Object.getOwnPropertyNames:', JSON.stringify(k));
|
|
});
|
|
|
|
/*
|
|
* The virtual 'name' of a lightfunc has the form:
|
|
*
|
|
* light_<ptr>_<flags>
|
|
*
|
|
* The flags field includes the magic value and other flags. The magic
|
|
* value is an important part of a function identity because it may have
|
|
* a large impact on the Duktape/C function behavior.
|
|
*/
|
|
|
|
t = /^light_[0-9a-fA-F]+_[0-9a-fA-F]{4}$/.exec(lightFunc.name);
|
|
print('lightFunc.name matches regexp:', (t !== null));
|
|
|
|
t = lightFunc.name.substring(0, 5) + '_' +
|
|
lightFunc.name.substring(6).replace(/[0-9a-fA-F]+/g, 'XXX');
|
|
print('censored lightFunc.name:', t);
|
|
}
|
|
|
|
try {
|
|
print('property misc test');
|
|
propertyMiscTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
traceback test
|
|
URIError: invalid input
|
|
duk_bi_global.c:NNN
|
|
light_PTR_0011 light strict preventsyield
|
|
tracebackTest TESTCASE:NNN
|
|
global TESTCASE:NNN preventsyield
|
|
===*/
|
|
|
|
function tracebackTest() {
|
|
var err;
|
|
|
|
try {
|
|
decodeURIComponent('%x');
|
|
} catch (e) {
|
|
err = e;
|
|
}
|
|
|
|
// heavy sanitization
|
|
print(sanitizeTraceback(err.stack));
|
|
}
|
|
|
|
try {
|
|
print('traceback test');
|
|
tracebackTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Duktape.act() test
|
|
Error: for traceback
|
|
callback TESTCASE:NNN preventsyield
|
|
light_PTR_0212 light strict preventsyield
|
|
duktapeActTest TESTCASE:NNN
|
|
global TESTCASE:NNN preventsyield
|
|
-1 ["lineNumber","pc","function"] light_PTR_0011
|
|
-2 ["lineNumber","pc","function"] callback
|
|
-3 ["lineNumber","pc","function"] light_PTR_0212
|
|
-4 ["lineNumber","pc","function"] duktapeActTest
|
|
-5 ["lineNumber","pc","function"] global
|
|
===*/
|
|
|
|
function duktapeActTest() {
|
|
// This test assumes that Array.prototype.forEach() has been created
|
|
// as a lightfunc, so that it appears in the middle of the callstack.
|
|
|
|
[ 'foo' ].forEach(function callback(x) {
|
|
var e = new Error('for traceback');
|
|
var i;
|
|
var a;
|
|
|
|
print(sanitizeTraceback(e.stack));
|
|
|
|
for (i = -1; ; i--) {
|
|
a = Duktape.act(i);
|
|
if (!a) { break; }
|
|
print(i, Duktape.enc('jx', Object.getOwnPropertyNames(a)), sanitizeLfunc(a.function.name));
|
|
}
|
|
});
|
|
}
|
|
|
|
try {
|
|
print('Duktape.act() test');
|
|
duktapeActTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
exempt built-ins test
|
|
Math.max (is lightfunc): function true
|
|
eval: function false
|
|
yield: function false
|
|
resume: function false
|
|
require: function false
|
|
Object: function false
|
|
Function: function false
|
|
Array: function false
|
|
String: function false
|
|
Boolean: function false
|
|
Number: function false
|
|
Date: function false
|
|
RegExp: function false
|
|
Error: function false
|
|
EvalError: function false
|
|
RangeError: function false
|
|
ReferenceError: function false
|
|
SyntaxError: function false
|
|
TypeError: function false
|
|
URIError: function false
|
|
Proxy: function false
|
|
Duktape.Buffer: function false
|
|
Duktape.Pointer: function false
|
|
Duktape.Thread: function false
|
|
Duktape.Logger: function false
|
|
Duktape: object false
|
|
Math: object false
|
|
JSON: object false
|
|
===*/
|
|
|
|
function exemptBuiltinsTest() {
|
|
function f(v) {
|
|
return (typeof v) + ' ' + isLightFunc(v);
|
|
}
|
|
|
|
// this is converted to a lightfunc; print for ensuring isLightFunc() works
|
|
print('Math.max (is lightfunc):', f(Math.max));
|
|
|
|
/*
|
|
* These specific built-ins properties cannot be converted to lightfuncs
|
|
* because of internal asserts or because a user may write some property
|
|
* of the function (e.g. "require.id").
|
|
*/
|
|
|
|
print('eval:', f(eval));
|
|
print('yield:', f(Duktape.Thread.yield));
|
|
print('resume:', f(Duktape.Thread.resume));
|
|
print('require:', f(require));
|
|
|
|
/*
|
|
* These top-level constructor functions are never converted to lightfuncs
|
|
* because they have properties that cannot be virtualized.
|
|
*/
|
|
|
|
print('Object:', f(Object));
|
|
print('Function:', f(Function));
|
|
print('Array:', f(Array));
|
|
print('String:', f(String));
|
|
print('Boolean:', f(Boolean));
|
|
print('Number:', f(Number));
|
|
print('Date:', f(Date));
|
|
print('RegExp:', f(RegExp));
|
|
print('Error:', f(Error));
|
|
print('EvalError:', f(EvalError));
|
|
print('RangeError:', f(RangeError));
|
|
print('ReferenceError:', f(ReferenceError));
|
|
print('SyntaxError:', f(SyntaxError));
|
|
print('TypeError:', f(TypeError));
|
|
print('URIError:', f(URIError));
|
|
print('Proxy:', f(Proxy));
|
|
|
|
print('Duktape.Buffer:', f(Duktape.Buffer));
|
|
print('Duktape.Pointer:', f(Duktape.Pointer));
|
|
print('Duktape.Thread:', f(Duktape.Thread));
|
|
print('Duktape.Logger:', f(Duktape.Logger));
|
|
|
|
/*
|
|
* These globals are not functions at all.
|
|
*/
|
|
|
|
print('Duktape:', f(Duktape));
|
|
print('Math:', f(Math));
|
|
print('JSON:', f(JSON));
|
|
}
|
|
|
|
try {
|
|
print('exempt built-ins test');
|
|
exemptBuiltinsTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
getOwnPropertyNames() test
|
|
length,name
|
|
length
|
|
name
|
|
===*/
|
|
|
|
function getOwnPropertyNamesTest() {
|
|
var lfunc = Math.cos;
|
|
var names;
|
|
|
|
named = Object.getOwnPropertyNames(lfunc);
|
|
print(named);
|
|
if (named) {
|
|
named.forEach(function (x) { print(x); });
|
|
}
|
|
}
|
|
|
|
try {
|
|
print('getOwnPropertyNames() test');
|
|
getOwnPropertyNamesTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
getOwnPropertyDescriptor() test
|
|
key: name
|
|
value: string light_PTR_0511
|
|
writable: boolean false
|
|
enumerable: boolean false
|
|
configurable: boolean false
|
|
key: length
|
|
value: number 1
|
|
writable: boolean false
|
|
enumerable: boolean false
|
|
configurable: boolean false
|
|
key: nonExistent
|
|
no descriptor
|
|
===*/
|
|
|
|
function getOwnPropertyDescriptorTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
function test(key) {
|
|
print('key:', key);
|
|
var pd = Object.getOwnPropertyDescriptor(lfunc, key);
|
|
if (!pd) { print('no descriptor'); return; }
|
|
print('value:', typeof pd.value, typeof pd.value === 'string' ? sanitizeLfunc(pd.value) : pd.value);
|
|
print('writable:', typeof pd.writable, pd.writable);
|
|
print('enumerable:', typeof pd.enumerable, pd.enumerable);
|
|
print('configurable:', typeof pd.configurable, pd.configurable);
|
|
}
|
|
|
|
test('name');
|
|
test('length');
|
|
test('nonExistent');
|
|
}
|
|
|
|
try {
|
|
print('getOwnPropertyDescriptor() test');
|
|
getOwnPropertyDescriptorTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
hasOwnProperty() test
|
|
true
|
|
true
|
|
false
|
|
false
|
|
===*/
|
|
|
|
function hasOwnPropertyTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
print(Object.prototype.hasOwnProperty.call(lfunc, 'name'));
|
|
print(Object.prototype.hasOwnProperty.call(lfunc, 'length'));
|
|
print(Object.prototype.hasOwnProperty.call(lfunc, 'nonExistent'));
|
|
print(Object.prototype.hasOwnProperty.call(lfunc, 'call')); // inherited
|
|
}
|
|
|
|
try {
|
|
print('hasOwnProperty() test');
|
|
hasOwnPropertyTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
propertyIsEnumerable() test
|
|
false
|
|
false
|
|
false
|
|
false
|
|
===*/
|
|
|
|
function propertyIsEnumerableTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
print(Object.prototype.propertyIsEnumerable(lfunc, 'name'));
|
|
print(Object.prototype.propertyIsEnumerable(lfunc, 'length'));
|
|
print(Object.prototype.propertyIsEnumerable(lfunc, 'nonExistent'));
|
|
print(Object.prototype.propertyIsEnumerable(lfunc, 'call')); // inherited
|
|
}
|
|
|
|
try {
|
|
print('propertyIsEnumerable() test');
|
|
propertyIsEnumerableTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
defineProperty() test
|
|
nonexistent: success
|
|
name: TypeError
|
|
length: success
|
|
===*/
|
|
|
|
function definePropertyTest() {
|
|
/*
|
|
* Object.defineProperty() specification algorithm throws a TypeError if
|
|
* the argument is not an object. Because we want lightfuncs to behave
|
|
* like full objects, Object.defineProperty() should work as if it was
|
|
* allowed and the lightfunc was an actual object.
|
|
*
|
|
* The current implementation just coerces a lightfunc to an object with
|
|
* ToObject(). The resulting normal Function object has a copy of the
|
|
* lightfunc's virtual properties. The Function object is also extensible
|
|
* (which is a bit odd because the lightfunc is not considered extensible).
|
|
* Because the coerced function is extensible, adding new properties will
|
|
* appear to succeed. The temporary object is not exposed to calling code
|
|
* and is later garbage collected.
|
|
*
|
|
* Another possible behavior might be an unconditional TypeError. This
|
|
* would not match the ordinary defineProperty() behavior where it is
|
|
* possible to write even to a write-protected value if the new value
|
|
* SameValue() compares equal to the old value.
|
|
*
|
|
* The preferred behavior is not clear.
|
|
*
|
|
* Drawing some analogies from plain strings: plain string values can be
|
|
* assigned properties with no error, even in strict mode:
|
|
*
|
|
* > function () { 'use strict'; return ('foo').bar=1; }()
|
|
* 1
|
|
*
|
|
* There is no analogy for defineProperty() because strings can't be used
|
|
* as an Object.defineProperty() argument:
|
|
*
|
|
* > Object.defineProperty('foo', 'bar', { value: 1, writable: true, ... })
|
|
* TypeError: Object.defineProperty called on non-object
|
|
*
|
|
* The extensibility of a plain string object also cannot be queried:
|
|
*
|
|
* > Object.isExtensible('foo')
|
|
* TypeError: Object.isExtensible called on non-object
|
|
*
|
|
* If a plain string is coerced to a full String object, it becomes
|
|
* extensible:
|
|
*
|
|
* > Object.isExtensible(new String('foo'))
|
|
* true
|
|
*
|
|
* So, the current behavior for a defineProperty() call is basically:
|
|
*
|
|
* Object.defineProperty(Object(lightFunc), ...);
|
|
*
|
|
* This will succeed in defining new properties (which are not reflected
|
|
* in the lightFunc value) which are effectively lost.
|
|
*/
|
|
|
|
var lf = Math.max;
|
|
|
|
// Non-existent property: succeeds because the temporary ToObject()
|
|
// coerced object is extensible. It might be more intuitive for this
|
|
// to fail with "not extensible".
|
|
try {
|
|
Object.defineProperty(lf, 'nonexistent', {
|
|
value: 123, writable: true, enumerable: true, configurable: true
|
|
});
|
|
print('nonexistent:', 'success');
|
|
} catch (e) {
|
|
print('nonexistent:', e.name);
|
|
}
|
|
|
|
// Existing non-configurable property with a different value: rejected
|
|
// as part of normal defineProperty() handling.
|
|
try {
|
|
Object.defineProperty(lf, 'name', {
|
|
value: 123, writable: true, enumerable: true, configurable: true
|
|
});
|
|
print('name:', 'success');
|
|
} catch (e) {
|
|
print('name:', e.name);
|
|
}
|
|
|
|
// Existing non-configurable property with same value as before: accepted
|
|
// as part of normal defineProperty() handling.
|
|
try {
|
|
Object.defineProperty(lf, 'length', {
|
|
value: 2, writable: false, enumerable: false, configurable: false
|
|
});
|
|
print('length:', 'success');
|
|
} catch (e) {
|
|
print('length:', e.name);
|
|
}
|
|
}
|
|
|
|
try {
|
|
print('defineProperty() test');
|
|
definePropertyTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
defineProperties() test
|
|
nonexistent: success
|
|
name: TypeError
|
|
length: success
|
|
===*/
|
|
|
|
function definePropertiesTest() {
|
|
/*
|
|
* Same as definePropertyTest() but for Object.defineProperties: it has
|
|
* a slightly different internal code path so should be tested separately.
|
|
*/
|
|
|
|
var lf = Math.max;
|
|
|
|
// Non-existent property.
|
|
try {
|
|
Object.defineProperties(lf, { nonexistent: {
|
|
value: 123, writable: true, enumerable: true, configurable: true
|
|
} });
|
|
print('nonexistent:', 'success');
|
|
} catch (e) {
|
|
print('nonexistent:', e.name);
|
|
}
|
|
|
|
// Existing non-configurable property, different value
|
|
try {
|
|
Object.defineProperties(lf, { name: {
|
|
value: 123, writable: true, enumerable: true, configurable: true
|
|
} });
|
|
print('name:', 'success');
|
|
} catch (e) {
|
|
print('name:', e.name);
|
|
}
|
|
|
|
// Existing non-configurable property, same value
|
|
try {
|
|
Object.defineProperties(lf, { length: {
|
|
value: 2, writable: false, enumerable: false, configurable: false
|
|
} });
|
|
print('length:', 'success');
|
|
} catch (e) {
|
|
print('length:', e.name);
|
|
}
|
|
}
|
|
|
|
try {
|
|
print('defineProperties() test');
|
|
definePropertiesTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
getPrototypeOf() test
|
|
true
|
|
true
|
|
===*/
|
|
|
|
function getPrototypeOfTest() {
|
|
var lfunc = Math.max;
|
|
|
|
print(Object.getPrototypeOf(lfunc) === Function.prototype);
|
|
print(lfunc.__proto__ === Function.prototype);
|
|
}
|
|
|
|
try {
|
|
print('getPrototypeOf() test');
|
|
getPrototypeOfTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
setPrototypeOf() test
|
|
TypeError
|
|
TypeError
|
|
TypeError
|
|
TypeError
|
|
success
|
|
success
|
|
success
|
|
success
|
|
===*/
|
|
|
|
function setPrototypeOfTest() {
|
|
var lfunc = Math.max;
|
|
var nonext;
|
|
|
|
function err(cb) {
|
|
try {
|
|
cb();
|
|
print('never here');
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
}
|
|
|
|
function succ(cb) {
|
|
try {
|
|
cb();
|
|
print('success');
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
}
|
|
|
|
// Trying to change prototype is a TypeError. The same behavior applies
|
|
// to ordinary non-extensible objects.
|
|
|
|
err(function () { var nonext = {}; Object.preventExtensions(nonext); Object.setPrototypeOf(nonext, {}); });
|
|
err(function () { Object.setPrototypeOf(lfunc, {}); });
|
|
|
|
err(function () { var nonext = {}; Object.preventExtensions(nonext); nonext.__proto__ = {}; });
|
|
err(function () { lfunc.__proto__ = {}; });
|
|
|
|
// Setting existing prototype value is a no-op.
|
|
|
|
succ(function () { var nonext = {}; Object.preventExtensions(nonext); Object.setPrototypeOf(nonext, Object.prototype); });
|
|
succ(function () { Object.setPrototypeOf(lfunc, Function.prototype); });
|
|
|
|
succ(function () { var nonext = {}; Object.preventExtensions(nonext); nonext.__proto__ = Object.prototype; });
|
|
succ(function () { lfunc.__proto__ = Function.prototype; });
|
|
}
|
|
|
|
try {
|
|
print('setPrototypeOf() test');
|
|
setPrototypeOfTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Array built-in test
|
|
Array: object [{_func:true}]
|
|
new Array: object [{_func:true}]
|
|
isArray: boolean false
|
|
toString: string "[object Function]"
|
|
valueOf: function {_func:true}
|
|
concat: object [{_func:true}]
|
|
pop: TypeError
|
|
push: TypeError
|
|
sort: function {_func:true}
|
|
splice: TypeError
|
|
reverse: function {_func:true}
|
|
shift: TypeError
|
|
unshift: TypeError
|
|
every: TypeError
|
|
some: TypeError
|
|
forEach: TypeError
|
|
map: TypeError
|
|
filter: TypeError
|
|
reduce: TypeError
|
|
reduceRight: TypeError
|
|
===*/
|
|
|
|
function arrayBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
function f(meth) {
|
|
testTypedJx(function () {
|
|
return Array.prototype[meth].call(lfunc);
|
|
}, meth);
|
|
}
|
|
|
|
testTypedJx(function () { return Array(lfunc) }, 'Array');
|
|
testTypedJx(function () { return new Array(lfunc) }, 'new Array');
|
|
testTypedJx(function () { return Array.isArray(lfunc) }, 'isArray');
|
|
|
|
// these tests are not particularly useful, but might reveal some
|
|
// assertion errors
|
|
|
|
f('toString');
|
|
f('valueOf');
|
|
f('concat');
|
|
f('pop'); // TypeError, length not writable
|
|
f('push'); // TypeError, length not writable
|
|
f('sort');
|
|
f('splice'); // TypeError, length not writable
|
|
f('reverse');
|
|
f('shift'); // TypeError, length not writable
|
|
f('unshift'); // TypeError, length not writable
|
|
f('every'); // TypeError, callback not a function
|
|
f('some'); // -''-
|
|
f('forEach'); // -''-
|
|
f('map'); // -''-
|
|
f('filter'); // -''-
|
|
f('reduce'); // -''-
|
|
f('reduceRight'); // -''-
|
|
}
|
|
|
|
try {
|
|
print('Array built-in test');
|
|
arrayBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Boolean built-in test
|
|
Boolean: boolean true
|
|
new Boolean: object true
|
|
toString: TypeError
|
|
valueOf: TypeError
|
|
===*/
|
|
|
|
function booleanBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return Boolean(lfunc) }, 'Boolean');
|
|
testTypedJx(function () { return new Boolean(lfunc) }, 'new Boolean');
|
|
testTypedJx(function () { return Boolean.prototype.toString.call(lfunc, lfunc) }, 'toString');
|
|
testTypedJx(function () { return Boolean.prototype.valueOf.call(lfunc, lfunc) }, 'valueOf');
|
|
}
|
|
|
|
try {
|
|
print('Boolean built-in test');
|
|
booleanBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Duktape.Buffer built-in test
|
|
Duktape.Buffer: TypeError
|
|
new Duktape.buffer: TypeError
|
|
toString: TypeError
|
|
valueOf: TypeError
|
|
===*/
|
|
|
|
function duktapeBufferBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return Duktape.Buffer(lfunc) }, 'Duktape.Buffer');
|
|
testTypedJx(function () { return new Duktape.Buffer(lfunc) }, 'new Duktape.buffer');
|
|
testTypedJx(function () { return Duktape.Buffer.prototype.toString.call(lfunc, lfunc) }, 'toString');
|
|
testTypedJx(function () { return Duktape.Buffer.prototype.valueOf.call(lfunc, lfunc) }, 'valueOf');
|
|
}
|
|
|
|
try {
|
|
print('Duktape.Buffer built-in test');
|
|
duktapeBufferBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Date built-in test
|
|
Date: string "string"
|
|
new Date: string "object"
|
|
parse: number NaN
|
|
UTC: number NaN
|
|
now: string "number"
|
|
toString: TypeError
|
|
valueOf: TypeError
|
|
toDateString: TypeError
|
|
toTimeString: TypeError
|
|
toLocaleString: TypeError
|
|
toLocaleDateString: TypeError
|
|
toLocaleTimeString: TypeError
|
|
getTime: TypeError
|
|
getFullYear: TypeError
|
|
getUTCFullYear: TypeError
|
|
getMonth: TypeError
|
|
getUTCFullMonth: TypeError
|
|
getDate: TypeError
|
|
getUTCDate: TypeError
|
|
getDay: TypeError
|
|
getUTCDay: TypeError
|
|
getHours: TypeError
|
|
getUTCHours: TypeError
|
|
getMinutes: TypeError
|
|
getUTCMinutes: TypeError
|
|
getSeconds: TypeError
|
|
getUTCSeconds: TypeError
|
|
getMilliseconds: TypeError
|
|
getUTCMilliseconds: TypeError
|
|
getTimezoneOffset: TypeError
|
|
setTime: TypeError
|
|
setMilliseconds: TypeError
|
|
setUTCMilliseconds: TypeError
|
|
setSeconds: TypeError
|
|
setUTCSeconds: TypeError
|
|
setMinutes: TypeError
|
|
setUTCMinutes: TypeError
|
|
setHours: TypeError
|
|
setUTCHours: TypeError
|
|
setDate: TypeError
|
|
setUTCDate: TypeError
|
|
setMonth: TypeError
|
|
setUTCMonth: TypeError
|
|
setFullYear: TypeError
|
|
setUTCFullYear: TypeError
|
|
toUTCString: TypeError
|
|
toISOString: TypeError
|
|
toJSON: TypeError
|
|
setYear: TypeError
|
|
getYear: TypeError
|
|
===*/
|
|
|
|
function dateBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
function f(meth) {
|
|
testTypedJx(function () {
|
|
return Date.prototype[meth].call(lfunc, lfunc);
|
|
}, meth);
|
|
}
|
|
|
|
testTypedJx(function () { return typeof Date(lfunc) }, 'Date');
|
|
testTypedJx(function () { return typeof new Date(lfunc) }, 'new Date');
|
|
testTypedJx(function () { return Date.parse(lfunc) }, 'parse');
|
|
testTypedJx(function () { return Date.UTC(lfunc) }, 'UTC');
|
|
testTypedJx(function () { return typeof Date.now(lfunc) }, 'now');
|
|
|
|
f('toString');
|
|
f('valueOf');
|
|
f('toDateString');
|
|
f('toTimeString');
|
|
f('toLocaleString');
|
|
f('toLocaleDateString');
|
|
f('toLocaleTimeString');
|
|
f('getTime');
|
|
f('getFullYear');
|
|
f('getUTCFullYear');
|
|
f('getMonth');
|
|
f('getUTCFullMonth');
|
|
f('getDate');
|
|
f('getUTCDate');
|
|
f('getDay');
|
|
f('getUTCDay');
|
|
f('getHours');
|
|
f('getUTCHours');
|
|
f('getMinutes');
|
|
f('getUTCMinutes');
|
|
f('getSeconds');
|
|
f('getUTCSeconds');
|
|
f('getMilliseconds');
|
|
f('getUTCMilliseconds');
|
|
f('getTimezoneOffset');
|
|
f('setTime');
|
|
f('setMilliseconds');
|
|
f('setUTCMilliseconds');
|
|
f('setSeconds');
|
|
f('setUTCSeconds');
|
|
f('setMinutes');
|
|
f('setUTCMinutes');
|
|
f('setHours');
|
|
f('setUTCHours');
|
|
f('setDate');
|
|
f('setUTCDate');
|
|
f('setMonth');
|
|
f('setUTCMonth');
|
|
f('setFullYear');
|
|
f('setUTCFullYear');
|
|
f('toUTCString');
|
|
f('toISOString');
|
|
f('toJSON');
|
|
f('setYear');
|
|
f('getYear');
|
|
}
|
|
|
|
try {
|
|
print('Date built-in test');
|
|
dateBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Duktape built-in test
|
|
info: object [9]
|
|
act: undefined undefined
|
|
gc: boolean true
|
|
fin-get: TypeError
|
|
fin-set: TypeError
|
|
encdec-hex: string "function light_PTR_0511() {(* light *)}"
|
|
dec-hex: TypeError
|
|
compact: function {_func:true}
|
|
===*/
|
|
|
|
function duktapeBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return Duktape.info(lfunc); }, 'info');
|
|
|
|
// doesn't really make sense
|
|
testTypedJx(function () { return Duktape.act(lfunc); }, 'act');
|
|
|
|
// doesn't really make sense (will act like Duktape.gc(0))
|
|
testTypedJx(function () { return Duktape.gc(lfunc); }, 'gc');
|
|
|
|
// attempt to get finalizer
|
|
testTypedJx(function () { return Duktape.fin(lfunc); }, 'fin-get');
|
|
|
|
// attempt to set finalizer
|
|
testTypedJx(function () { return Duktape.fin(lfunc, function () {}); }, 'fin-set');
|
|
|
|
testTypedJx(function () { return sanitizeLfunc(Duktape.dec('hex', Duktape.enc('hex', lfunc))); }, 'encdec-hex');
|
|
testTypedJx(function () { return Duktape.dec('hex', lfunc); }, 'dec-hex');
|
|
|
|
// attempt to compact is a no-op
|
|
testTypedJx(function () { return Duktape.compact(lfunc); }, 'compact');
|
|
}
|
|
|
|
try {
|
|
print('Duktape built-in test');
|
|
duktapeBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Error built-in test
|
|
Error: object {}
|
|
new Error: object {}
|
|
toString: string "light_PTR_0511"
|
|
valueOf: function {_func:true}
|
|
===*/
|
|
|
|
function errorBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return Error(lfunc); }, 'Error');
|
|
testTypedJx(function () { return new Error(lfunc); }, 'new Error');
|
|
testTypedJx(function () { return sanitizeLfunc(Error.prototype.toString.call(lfunc, lfunc)); }, 'toString');
|
|
testTypedJx(function () { return Error.prototype.valueOf.call(lfunc, lfunc); }, 'valueOf');
|
|
}
|
|
|
|
try {
|
|
print('Error built-in test');
|
|
errorBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Function built-in test
|
|
Function: function {_func:true}
|
|
new Function: function {_func:true}
|
|
toString: string "function light_PTR_0511() {(* light *)}"
|
|
valueOf: function {_func:true}
|
|
===*/
|
|
|
|
function functionBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return Function(lfunc); }, 'Function');
|
|
testTypedJx(function () { return new Function(lfunc); }, 'new Function');
|
|
testTypedJx(function () { return sanitizeLfunc(Function.prototype.toString.call(lfunc, lfunc)) }, 'toString');
|
|
testTypedJx(function () { return Function.prototype.valueOf.call(lfunc, lfunc) }, 'valueOf');
|
|
|
|
// Already covered by other tests:
|
|
// - call
|
|
// - apply
|
|
// - bind
|
|
}
|
|
|
|
try {
|
|
print('Function built-in test');
|
|
functionBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
global built-in test
|
|
eval: function {_func:true}
|
|
parseInt: number NaN
|
|
parseFloat: number NaN
|
|
isNaN: boolean true
|
|
isFinite: boolean false
|
|
decodeURI: string "function light_PTR_0511() {(* light *)}"
|
|
decodeURIComponent: string "function light_PTR_0511() {(* light *)}"
|
|
encodeURI: string "string"
|
|
encodeURIComponent: string "string"
|
|
escape: string "string"
|
|
unescape: string "string"
|
|
===*/
|
|
|
|
function globalBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
// This interestingly evaluates to a function because ToString(lfunc) parses
|
|
// to a valid function declaration.
|
|
testTypedJx(function () { return eval(lfunc); }, 'eval');
|
|
|
|
testTypedJx(function () { return parseInt(lfunc); }, 'parseInt');
|
|
testTypedJx(function () { return parseFloat(lfunc); }, 'parseFloat');
|
|
testTypedJx(function () { return isNaN(lfunc); }, 'isNaN');
|
|
testTypedJx(function () { return isFinite(lfunc); }, 'isFinite');
|
|
|
|
// Must sanitize here
|
|
testTypedJx(function () { return sanitizeLfunc(decodeURI(lfunc)); }, 'decodeURI');
|
|
testTypedJx(function () { return sanitizeLfunc(decodeURIComponent(lfunc)); }, 'decodeURIComponent');
|
|
|
|
// The encoded output would need to be sanitized; just check it's a string
|
|
testTypedJx(function () { return typeof encodeURI(lfunc); }, 'encodeURI');
|
|
testTypedJx(function () { return typeof encodeURIComponent(lfunc); }, 'encodeURIComponent');
|
|
testTypedJx(function () { return typeof escape(lfunc); }, 'escape');
|
|
testTypedJx(function () { return typeof unescape(lfunc); }, 'unescape');
|
|
}
|
|
|
|
try {
|
|
print('global built-in test');
|
|
globalBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
JSON built-in test
|
|
parse: SyntaxError
|
|
stringify: undefined undefined
|
|
===*/
|
|
|
|
function jsonBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
// covered elsewhere too
|
|
testTypedJx(function () { return JSON.parse(lfunc); }, 'parse');
|
|
testTypedJx(function () { return JSON.stringify(lfunc); }, 'stringify');
|
|
}
|
|
|
|
try {
|
|
print('JSON built-in test');
|
|
jsonBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Duktape.Logger built-in test
|
|
Duktape.Logger: TypeError
|
|
new Duktape.Logger: object {}
|
|
fmt: TypeError
|
|
raw: TypeError
|
|
TIMESTAMP INF test: My light func is: function light_PTR_0511() {(* light *)}
|
|
===*/
|
|
|
|
function duktapeLoggerBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
var old_raw;
|
|
var logger;
|
|
|
|
testTypedJx(function () { return Duktape.Logger(lfunc); }, 'Duktape.Logger');
|
|
testTypedJx(function () { return new Duktape.Logger(lfunc); }, 'new Duktape.Logger');
|
|
testTypedJx(function () { return Duktape.logger.prototype.fmt(lfunc); }, 'fmt');
|
|
testTypedJx(function () { return Duktape.logger.prototype.raw(lfunc); }, 'raw');
|
|
|
|
// Test that lightfuncs log in a useful way. Because the toString()
|
|
// coercion contains a pointer we need to abduct the raw() function.
|
|
|
|
old_raw = Duktape.Logger.prototype.old_raw;
|
|
Duktape.Logger.prototype.raw = function (buf) {
|
|
var msg = sanitizeLfunc(String(buf));
|
|
msg = msg.replace(/^\S+/, 'TIMESTAMP');
|
|
print(msg);
|
|
};
|
|
logger = new Duktape.Logger('test');
|
|
logger.info('My light func is:', lfunc);
|
|
Duktape.Logger.prototype.raw = old_raw;
|
|
}
|
|
|
|
try {
|
|
print('Duktape.Logger built-in test');
|
|
duktapeLoggerBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Math built-in test
|
|
abs: number NaN
|
|
acos: number NaN
|
|
asin: number NaN
|
|
atan: number NaN
|
|
atan2: number NaN
|
|
ceil: number NaN
|
|
cos: number NaN
|
|
exp: number NaN
|
|
floor: number NaN
|
|
log: number NaN
|
|
max: number NaN
|
|
min: number NaN
|
|
pow: number NaN
|
|
random: string "number"
|
|
round: number NaN
|
|
sin: number NaN
|
|
sqrt: number NaN
|
|
tan: number NaN
|
|
===*/
|
|
|
|
function mathBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return Math.abs(lfunc); }, 'abs');
|
|
testTypedJx(function () { return Math.acos(lfunc); }, 'acos');
|
|
testTypedJx(function () { return Math.asin(lfunc); }, 'asin');
|
|
testTypedJx(function () { return Math.atan(lfunc); }, 'atan');
|
|
testTypedJx(function () { return Math.atan2(lfunc); }, 'atan2');
|
|
testTypedJx(function () { return Math.ceil(lfunc); }, 'ceil');
|
|
testTypedJx(function () { return Math.cos(lfunc); }, 'cos');
|
|
testTypedJx(function () { return Math.exp(lfunc); }, 'exp');
|
|
testTypedJx(function () { return Math.floor(lfunc); }, 'floor');
|
|
testTypedJx(function () { return Math.log(lfunc); }, 'log');
|
|
testTypedJx(function () { return Math.max(lfunc); }, 'max');
|
|
testTypedJx(function () { return Math.min(lfunc); }, 'min');
|
|
testTypedJx(function () { return Math.pow(lfunc); }, 'pow');
|
|
testTypedJx(function () { return typeof Math.random(lfunc); }, 'random'); // avoid outputting result value
|
|
testTypedJx(function () { return Math.round(lfunc); }, 'round');
|
|
testTypedJx(function () { return Math.sin(lfunc); }, 'sin');
|
|
testTypedJx(function () { return Math.sqrt(lfunc); }, 'sqrt');
|
|
testTypedJx(function () { return Math.tan(lfunc); }, 'tan');
|
|
}
|
|
|
|
try {
|
|
print('Math built-in test');
|
|
mathBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Number built-in test
|
|
Number: number NaN
|
|
new Number: object NaN
|
|
toString: TypeError
|
|
toLocaleString: TypeError
|
|
valueOf: TypeError
|
|
toFixed: TypeError
|
|
toExponential: TypeError
|
|
toPrecision: TypeError
|
|
===*/
|
|
|
|
function numberBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return Number(lfunc); }, 'Number');
|
|
testTypedJx(function () { return new Number(lfunc); }, 'new Number');
|
|
testTypedJx(function () { return Number.prototype.toString.call(lfunc, lfunc); }, 'toString');
|
|
testTypedJx(function () { return Number.prototype.toLocaleString.call(lfunc, lfunc); }, 'toLocaleString');
|
|
testTypedJx(function () { return Number.prototype.valueOf.call(lfunc, lfunc); }, 'valueOf');
|
|
testTypedJx(function () { return Number.prototype.toFixed.call(lfunc, lfunc); }, 'toFixed');
|
|
testTypedJx(function () { return Number.prototype.toExponential.call(lfunc, lfunc); }, 'toExponential');
|
|
testTypedJx(function () { return Number.prototype.toPrecision.call(lfunc, lfunc); }, 'toPrecision');
|
|
}
|
|
|
|
try {
|
|
print('Number built-in test');
|
|
numberBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Object built-in test
|
|
Object: function {_func:true}
|
|
new Object: function {_func:true}
|
|
getPrototypeOf: function {_func:true}
|
|
setPrototypeOf: TypeError
|
|
seal: function {_func:true}
|
|
freeze: function {_func:true}
|
|
preventExtensions: function {_func:true}
|
|
isSealed: boolean true
|
|
isFrozen: boolean true
|
|
isExtensible: boolean false
|
|
toString: string "[object Function]"
|
|
toLocaleString: string "function light_PTR_0511() {(* native *)}"
|
|
valueOf: function {_func:true}
|
|
isPrototypeOf: boolean false
|
|
===*/
|
|
|
|
function objectBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
// Object coercion: return ToObject, i.e. normal function
|
|
testTypedJx(function () { return Object(lfunc); }, 'Object');
|
|
|
|
// new Object(x) is supposed to return 'x' as is, if it is already a
|
|
// function. This is tricky for lightfuncs: should the input be returned
|
|
// as is (= a lightfunc) or ToObject coerced version of input (not a
|
|
// lightfunc)? At the moment ToObject(lightfunc) is returned.
|
|
|
|
testTypedJx(function () { return new Object(lfunc); }, 'new Object');
|
|
testTypedJx(function () { return Object.getPrototypeOf(lfunc); }, 'getPrototypeOf');
|
|
testTypedJx(function () { return Object.setPrototypeOf(lfunc, {}); }, 'setPrototypeOf');
|
|
testTypedJx(function () { return Object.seal(lfunc); }, 'seal');
|
|
testTypedJx(function () { return Object.freeze(lfunc); }, 'freeze');
|
|
testTypedJx(function () { return Object.preventExtensions(lfunc); }, 'preventExtensions');
|
|
testTypedJx(function () { return Object.isSealed(lfunc); }, 'isSealed');
|
|
testTypedJx(function () { return Object.isFrozen(lfunc); }, 'isFrozen');
|
|
testTypedJx(function () { return Object.isExtensible(lfunc); }, 'isExtensible');
|
|
|
|
// Covered elsewhere:
|
|
// - getOwnPropertyDescriptor()
|
|
// - defineProperty()
|
|
// - defineProperties()
|
|
// - keys()
|
|
|
|
testTypedJx(function () { return Object.prototype.toString.call(lfunc, lfunc); }, 'toString');
|
|
testTypedJx(function () { return sanitizeLfunc(Object.prototype.toLocaleString.call(lfunc, lfunc)); }, 'toLocaleString');
|
|
testTypedJx(function () { return Object.prototype.valueOf.call(lfunc, lfunc); }, 'valueOf');
|
|
testTypedJx(function () { return Object.prototype.isPrototypeOf.call(lfunc, lfunc); }, 'isPrototypeOf');
|
|
}
|
|
|
|
try {
|
|
print('Object built-in test');
|
|
objectBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Duktape.Pointer built-in test
|
|
Duktape.Pointer: pointer (null)
|
|
new Duktape.Pointer: object (null)
|
|
toString: TypeError
|
|
valueOf: TypeError
|
|
===*/
|
|
|
|
function duktapePointerBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return Duktape.Pointer(lfunc); }, 'Duktape.Pointer');
|
|
testTypedJx(function () { return new Duktape.Pointer(lfunc); }, 'new Duktape.Pointer');
|
|
testTypedJx(function () { return Duktape.Pointer.prototype.toString.call(lfunc); }, 'toString');
|
|
testTypedJx(function () { return Duktape.Pointer.prototype.toString.call(lfunc); }, 'valueOf');
|
|
}
|
|
|
|
try {
|
|
print('Duktape.Pointer built-in test');
|
|
duktapePointerBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Proxy built-in test
|
|
get
|
|
this: object false [object Object]
|
|
target: function false function light_PTR_0511() {(* native *)}
|
|
key: string name
|
|
proxy.name: light_PTR_0511
|
|
get
|
|
this: object false [object Object]
|
|
target: function false function light_PTR_0511() {(* native *)}
|
|
key: string length
|
|
proxy.length: 1
|
|
get
|
|
this: object false [object Object]
|
|
target: function false function light_PTR_0511() {(* native *)}
|
|
key: string nonExistent
|
|
proxy.nonExistent: dummy
|
|
get
|
|
this: function false function light_PTR_0511() {(* native *)}
|
|
target: object false [object Object]
|
|
key: string foo
|
|
proxy.foo: bar
|
|
get
|
|
this: function false function light_PTR_0511() {(* native *)}
|
|
target: object false [object Object]
|
|
key: string nonExistent
|
|
proxy.nonExistent: dummy
|
|
===*/
|
|
|
|
function proxyBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
// Proxy as a target object; ES6 requires that must be an Object and a
|
|
// lightfunc pretends to be an object. So, it must be possible to use
|
|
// lightfunc as a target. Currently Proxy will just coerce the lightfunc
|
|
// to a full Function silently.
|
|
|
|
var handler = {}
|
|
var proxy = new Proxy(lfunc, handler);
|
|
handler.get = function (target, key) {
|
|
print('get');
|
|
print('this:', typeof this, isLightFunc(this), sanitizeLfunc(this));
|
|
print('target:', typeof target, isLightFunc(target), sanitizeLfunc(target));
|
|
print('key:', typeof key, key);
|
|
return target[key] || 'dummy'; // passthrough
|
|
}
|
|
print('proxy.name:', sanitizeLfunc(proxy.name));
|
|
print('proxy.length:', proxy.length);
|
|
print('proxy.nonExistent:', proxy.nonExistent);
|
|
|
|
// Proxy as a handler value; ES6 requires it must be an Object and a
|
|
// lightfunc pretends to be an object. The traps must be placed in
|
|
// Function.prototype for it to actually work - so this is not a very
|
|
// useful thing. Currently Proxy will just coerce the lightfunc to a
|
|
// full Function silently.
|
|
|
|
var proxy = new Proxy({ foo: 'bar' }, lfunc);
|
|
Function.prototype.get = function (target, key) {
|
|
print('get');
|
|
print('this:', typeof this, isLightFunc(this), sanitizeLfunc(this));
|
|
print('target:', typeof target, isLightFunc(target), target);
|
|
print('key:', typeof key, key);
|
|
return target[key] || 'dummy'; // passthrough
|
|
};
|
|
print('proxy.foo:', proxy.foo);
|
|
print('proxy.nonExistent:', proxy.nonExistent);
|
|
delete Function.prototype.get;
|
|
}
|
|
|
|
try {
|
|
print('Proxy built-in test');
|
|
proxyBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
RegExp built-in test
|
|
RegExp: SyntaxError
|
|
new RegExp: SyntaxError
|
|
exec: TypeError
|
|
test: TypeError
|
|
toString: TypeError
|
|
valueOf: function {_func:true}
|
|
===*/
|
|
|
|
function regexpBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return RegExp(lfunc); }, 'RegExp');
|
|
testTypedJx(function () { return new RegExp(lfunc); }, 'new RegExp');
|
|
|
|
testTypedJx(function () { return RegExp.prototype.exec.call(lfunc, lfunc); }, 'exec');
|
|
testTypedJx(function () { return RegExp.prototype.test.call(lfunc, lfunc); }, 'test');
|
|
testTypedJx(function () { return RegExp.prototype.toString.call(lfunc, lfunc); }, 'toString');
|
|
testTypedJx(function () { return RegExp.prototype.valueOf.call(lfunc, lfunc); }, 'valueOf');
|
|
}
|
|
|
|
try {
|
|
print('RegExp built-in test');
|
|
regexpBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
String built-in test
|
|
String: string "function light_PTR_0511() {(* light *)}"
|
|
new String: string "function light_PTR_0511() {(* light *)}"
|
|
new String: string "object"
|
|
fromCharCode: string "\x00"
|
|
toString: TypeError
|
|
valueOf: TypeError
|
|
charAt: string "f"
|
|
charCodeAt: number 102
|
|
concat: string "function light_PTR_0511() {(* light *)}function light_PTR_0511() {(* light *)}"
|
|
indexOf: number 0
|
|
lastIndexOf: number 0
|
|
localeCompare: number 0
|
|
match: SyntaxError
|
|
replace: string "undefined"
|
|
search: SyntaxError
|
|
slice: string "function light_PTR_0511() {(* light *)}"
|
|
split: object ["",""]
|
|
substring: string "function light_PTR_0511() {(* light *)}"
|
|
toLowerCase: string "function light_PTR_0511() {(* light *)}"
|
|
toLocaleLowerCase: string "function light_PTR_0511() {(* light *)}"
|
|
toUpperCase: string "FUNCTION LIGHT_PTR_0511() {(* LIGHT *)}"
|
|
toLocaleUpperCase: string "FUNCTION LIGHT_PTR_0511() {(* LIGHT *)}"
|
|
trim: string "function light_PTR_0511() {(* light *)}"
|
|
substr: string "function light_PTR_0511() {(* light *)}"
|
|
===*/
|
|
|
|
function stringBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
|
|
testTypedJx(function () { return sanitizeLfunc(String(lfunc)); }, 'String');
|
|
|
|
// new String() returns an object, but sanitizeLfunc() coerces it to a
|
|
// plain string; check return type separately
|
|
testTypedJx(function () { return sanitizeLfunc(new String(lfunc)); }, 'new String');
|
|
testTypedJx(function () { return typeof new String(lfunc); }, 'new String');
|
|
|
|
testTypedJx(function () { return String.fromCharCode(lfunc); }, 'fromCharCode');
|
|
|
|
testTypedJx(function () { return String.prototype.toString.call(lfunc, lfunc); }, 'toString');
|
|
testTypedJx(function () { return String.prototype.valueOf.call(lfunc, lfunc); }, 'valueOf');
|
|
testTypedJx(function () { return String.prototype.charAt.call(lfunc, lfunc); }, 'charAt');
|
|
testTypedJx(function () { return String.prototype.charCodeAt.call(lfunc, lfunc); }, 'charCodeAt');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.concat.call(lfunc, lfunc)); }, 'concat');
|
|
testTypedJx(function () { return String.prototype.indexOf.call(lfunc, lfunc); }, 'indexOf');
|
|
testTypedJx(function () { return String.prototype.lastIndexOf.call(lfunc, lfunc); }, 'lastIndexOf');
|
|
testTypedJx(function () { return String.prototype.localeCompare.call(lfunc, lfunc); }, 'localeCompare');
|
|
testTypedJx(function () { return String.prototype.match.call(lfunc, lfunc); }, 'match');
|
|
testTypedJx(function () { return String.prototype.replace.call(lfunc, lfunc); }, 'replace');
|
|
testTypedJx(function () { return String.prototype.search.call(lfunc, lfunc); }, 'search');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.slice.call(lfunc, lfunc)); }, 'slice');
|
|
testTypedJx(function () { return String.prototype.split.call(lfunc, lfunc); }, 'split');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.substring.call(lfunc, lfunc)); }, 'substring');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.toLowerCase.call(lfunc, lfunc)); }, 'toLowerCase');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.toLocaleLowerCase.call(lfunc, lfunc)); }, 'toLocaleLowerCase');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.toUpperCase.call(lfunc, lfunc)); }, 'toUpperCase');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.toLocaleUpperCase.call(lfunc, lfunc)); }, 'toLocaleUpperCase');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.trim.call(lfunc, lfunc)); }, 'trim');
|
|
testTypedJx(function () { return sanitizeLfunc(String.prototype.substr.call(lfunc, lfunc)); }, 'substr');
|
|
}
|
|
|
|
try {
|
|
print('String built-in test');
|
|
stringBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|
|
/*===
|
|
Duktape.Thread built-in test
|
|
TypeError
|
|
TypeError
|
|
===*/
|
|
|
|
function duktapeThreadBuiltinTest() {
|
|
var lfunc = Math.cos;
|
|
var thr;
|
|
|
|
// Lightfunc should be accepted as an initial function for a thread, but
|
|
// as of Duktape 1.0 only non-bound Ecmascript functions are allowed.
|
|
try {
|
|
thr = new Duktape.Thread(lfunc);
|
|
print(Duktape.Thread.resume(thr, 1.23));
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
try {
|
|
print(Duktape.Thread.yield(lfunc, 1.23));
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
}
|
|
|
|
try {
|
|
print('Duktape.Thread built-in test');
|
|
duktapeThreadBuiltinTest();
|
|
} catch (e) {
|
|
print(e.stack || e);
|
|
}
|
|
|