/* Basic object literal tests. The prototype-supplied methods
are tested in a separate script.
*/
const o = {
a: 1, b: 2,
c
:
3,
4:'four',
5: 5,
array: [2,4,6],
"removed": true
};
o.self = o;
o[((('d')))] = 42;
assert o === o.self;
assert 'array' === typename o.array;
assert 1 === o.a;
assert 2 === o['b'];
assert 3 === o.self['self'].self.c;
assert 'four' === o.4;
assert 5 === o[3+2];
//assert 5 === o.(3+2);
assert o.removed;
assert undefined === unset o.self.removed, o.removed /*does not fail on missing properties*/;
assert 'undefined' === typename o.removed;
//unset o.self /* cannot JSON-output cyclical objects */;
scope {
const proto = { a:0 }, o = { prototype:proto, a:1 };
assert o inherits proto;
assert 1 === o.a;
unset o.a;
assert 0 === o.a;
unset o.a;
assert 0 === o.a;
o.a = 1;
++o.a;
assert 2 === o.a;
o.a += -1;
assert 1 === o.a;
o.prototype = null /* 'null' or 'undefined' assignment removes a prototype... */;
assert undefined === o.prototype /* ... which looks like this afterwards */;
assert !(o inherits proto);
assert o !inherits proto;
assert 'CWAL_RC_TYPE' === catch{o.prototype = 1}.codeString();
}
scope {
const g = {x:0};
assert 1 === ++g.x;
assert ++g.x < 3 /* must not error b/c of missing "this" via dot op */;
assert g.x++ === 2 /* must not error b/c of missing "this" via dot op */;
// Former BUG:
g.x++ ? g.x-- : --g.x /* Must not error with: Unexpected LHS (X++ operation) for X?Y:Z operator. */;
g /* trailing expr. needed to trigger the bug. */;
;;
}
assert catch{{a:1, b:2, }}.codeString() === 'CWAL_SCR_SYNTAX';
assert catch{{a:1,, b:2, }}.codeString() === 'CWAL_SCR_SYNTAX';
assert catch{{,a:1, b:2, }}.codeString() === 'CWAL_SCR_SYNTAX';
assert catch{{,}}.codeString() === 'CWAL_SCR_SYNTAX';
// Bugfix 20171111: disallow trailing semicolon in value-part expressions:
assert catch{{a:1;}}.message.indexOf('semicolon')>0;
assert catch{{a:1;,b:0}}.message.indexOf('semicolon')>0;
scope {
/**
Added 20171201: JS-like shortcut syntax:
{a, b, c} ==> {a:a, b:b, c:c}
*/
const a = 1, b = "hi", c = [1,2,3];
const o = {a, x:4, b, c, y: 5};
assert 1 === o.a;
assert "hi" === o.b;
assert c === o.c;
assert 4 === o.x;
assert 5 === o.y;
assert catch{{x$x$x}}.codeString() === 'CWAL_RC_NOT_FOUND' /* unknown identifier */;
assert catch{{"x"}}.codeString() === 'CWAL_SCR_SYNTAX' /* non-identifier */;
}
scope {
/*
Added 20191117: use an expression as an object literal key:
const x = 'c';
const o = {a:1, b: 2, [x]: 3}
*/
const x = 'c';
const o = {prototype: null, a:1, b: 2, [x]: 3};
assert 3 === o[x];
assert 3 === o.c;
assert 1 === {
prototype:null,
[scope{
for(;;) break "hi, "+"world";
}]: 1
}["hi, world"];
assert 1 === {
["abc äbc"[4][0][0][0][0][0]]: 1
}.ä;
assert 0 === catch {{[x, break]: 1}}
.message.indexOf("Unhandled 'break' in object literal.");
assert 0 === catch {{[x, return]: 1}}
.message.indexOf("Unhandled 'return' in object literal.");
assert 0 === catch {{[x, continue]: 1}}
.message.indexOf("Unhandled 'continue' in object literal.");
;;
}
assert 999 === scope {
/*
2016-02-03: testing fix:
https://fossil.wanderinghorse.net/r/cwal/info/5041ab1deee33194
If it's broken, this will crash if built in debug mode,
triggering a cwal-level assertion, possibly a different one
depending on the type of the scope result value's type.
*/
{a: 999}.a
/* 2020-02-20: The code that was testing has since disappeared: it
seems that the related feature was no longer needed once cwal
added scope push/pop APIs and cwal_scope_pop2(), both of which
allowed that behaviour to be relegated to cwal. */
};
scope {
/** 2020-02-06: dot-length operator (lhs.#) now works on non-list
containers, resolving to the number of properties. */
assert 2 === import.# /* path+doPathSearch properties */;
assert 3 === {a:1, b:1, c:1}.#;
}
scope {
/** 2020-02-18: {x:=y} sets x as a const property. */
const p = {x:1};
const o = {a:1, b:=2, c:3, prototype:=undefined};
assert 2 === o.b;
assert 'CWAL_RC_CONST_VIOLATION' === catch {o.b = 1}.codeString();
assert p === (o.prototype = p)
/* consting the prototype has no effect b/c we don't have a way
to flag/enforce that constraint at the C level. */;
assert 0 === o.clearProperties().#
/* also removes const properties! Bug or feature? */;
assert 1 === (o.b=1 /* constness was lost via clearProperties() */);
}
scope {
/** 2020-02-18: x.y:=z sets x as a const property. */
const o= {a:1};
assert 2 === (o.a=2);
assert 3 === (o.a:=3);
assert 'CWAL_RC_CONST_VIOLATION' === catch {o.a = 1}.codeString();
assert 'CWAL_RC_CONST_VIOLATION' === catch {o['a'] = 1}.codeString();
assert 'CWAL_RC_CONST_VIOLATION' === catch {o.a := 1}.codeString();
assert 'CWAL_SCR_SYNTAX' === catch {o := 1}.codeString()
/* := op only works for property assignment. */;
assert 1 === o.#;
assert o === o.clearProperties();
assert 0 === o.#;
}
scope {
/** 2020-02-20: inline expansion of object properties into an
object literal, similar to JS's {...otherObj}, which it calls
"spread" syntax. We use the @ prefix because already use that
for @array expansion, which is semantically very similar to
what we use it for here.
*/
var o = {a: 1, b: 2, c:3};
var o2 = {@o};
assert o.# === o2.#;
foreach(o=>k,v) assert o2[k]==v;
o2 = {@o, c:4};
assert o.# === o2.#;
assert 4 === o2.c;
o2 = {a:4, @o, d:5};
assert o.#+1 === o2.#;
assert o.a === o2.a;
assert 5 === o2.d;
o2 = {d:5, @o};
assert o.#+1 === o2.#;
assert 5 === o2.d;
o2 = {@{a:1, b:2}};
assert 2 === o2.#;
assert 1 === o2.a;
assert 2 === o2.b;
o2 = {@proc(){return {a:1,b:2}}(), c:3};
assert 3 === o2.#;
assert 1 === o2.a;
assert 2 === o2.b;
assert 3 === o2.c;
var ex = catch{ {a:1, @, b:1} };
assert 'CWAL_SCR_SYNTAX' === ex.codeString();
assert ex.message.indexOf('empty expression') > 0;
ex = catch{ {@3} };
assert 'CWAL_RC_TYPE' === ex.codeString();
assert ex.message.indexOf('container') > 0;
assert 'CWAL_RC_CONST_VIOLATION' === catch o2 = {a:=1, @o}.codeString();
o2 = {@o, a:=3};
assert 3 === o2.a;
assert 'CWAL_RC_CONST_VIOLATION' === catch {o2.a =1}.codeString();
o = {
// Special case: constructor in an object literal is
// assigned as hidden/const, and hidden properties
// are not iterated over but do count towards the
// container.# operator...
__new:proc(){}
};
assert 1 === o.#;
o2 = {@o};
assert 0 === o2.#;
assert 'CWAL_RC_TYPE' === catch{
/*
The "problem" with enums is that they can use either object
or hash storage internally, so we would need to
differentiate between the two in the @ expansion. We don't
currently do that, but maybe someday will.
*/
{@enum {a,b,c}}
}.codeString();
}
o /* propagate top-most o out for lifetime checks */;