In 13.5, the if-as statement and if-as expression were quite "magical", able to apply a wide variety of conversions including function calls and constructors. This can create confusing and error-prone situations.
In 14.0, the if-as statement is limited to membership testing and unboxing conversions (see below). The if-as expression is now exactly equivalent in behaviour to the if-as statement.
public void foo(Object o) { // These two are equivalent if (o as Str) {pln(o.v);} if (o as str) {pln(o);} // These two are equivalent if (o as Int) {pln(o.v);} if (o as int) {pln(o);} // new in 14.0 // These two are equivalent in 14.0 if (o as int) {pln(o);} else {pln("not an int");} pln(o as int ? o : "not an int"); }
public class A { public constructor() {pln(this, " is created");} } public class B { public constructor(A a) {pln(this, " is created from A");} } public class C extends A { public constructor() {} public constructor(A a) {pln(this, " is created from A");} } public void foo(A a) { pln("a=", a); if (a as B) {pln("a as B=", a);} // forbidden in 14.0 if (a as C) {pln("a as C=", a);} } { foo(A()); pln(); foo(C()); }
Since B
does not inherit from A
, if (a as B)
is not valid in 14.0. It only works in 13.5 because there is a constructor which "converts" A
into a new instance of B
.
This happened in practice with DialogWindow
and Button
, respectively, since Button
has a constructor with one required argument of type Window
(and DialogWindow
is a Window
).
Output in 13.5
A(175) is created
a=A(175)
B(165) is created from A
a as B=B(165)
C(171) is created
a=C(171)
B(161) is created from A
a as B=B(161)
a as C=C(171)
Output in 14.0
A(36) is created
a=A(36)
C(79) is created
a=C(79)
a as C=C(79)
The compileFun
CM function has been replaced with a native
implementation, which is faster and cleaner. The remove
optional
argument has been removed as the new version avoids writing the code
to disk in any case.
Old: public Function compileFun(str def, bool output=false, bool remove=true) New: public Function compileFun(str def, bool output=false) Old: public Function compileFun(StrBuf def, bool output=false, bool remove=true) New: public Function compileFun(StrBuf def, bool output=false)
A similar function which returns all compiled functions in the file, rather than just the first one, has been added.
Added: public Function[] compileFuns(str def, bool output=false) Added: public Function[] compileFuns(StrBuf def, bool output=false)
The internal code responsible for finding the appropriate conversion path between types (if any) has been significantly improved. There is a small risk of surprising behaviour since the new algorithm performs an exhaustive search and selects the "best" path, rather than using flawed pruning heuristics.
In some circumstances, the type conversion search may recurse forever; the old version would treat this as a failure to find an appropriate conversion, the new version will treat this as an error.
Old: public bool cm.io.easySave(Object obj, Url url, bool pkgVersions=true, bool fast=false); New: public bool cm.io.easySave(Object obj, Url url, bool pkgVersions=true, bool fast=false, bool verifyCRC=false); Old: public bool cm.io.object.safeReplaceUrl(Url this, Url target, bool xtrace=false) New: public bool cm.io.object.safeReplaceUrl(Url this, Url target, bool verifyCRC=false, bool xtrace=false)
public void foo(FooClass this) { pln(field); // equivalent to this.field } public void foo(fooValue& this) { pln(field); // equivalent to this.field } public void foo(fooValue this) { pln(field); // equivalent to this.field in 14.0 }
The garbage collector (GC) has two different pause-states; "frozen"
and "parked". The former prevents almost all GC activity, the latter
forces the GC to finish any pending work and prevents all GC
activity. The GC interface now exposes gcUnPark
so that CM code can
enter and leave the "parked" state.
Added: gcUnPark();
14.0 makes many changes to the CM-DLL interface, you must rebuild all DLLs.
The CM-DLL interface now uses a secondary version check, allowing breaking API changes without necessarily changing the size of the DllStartInfo struct.
Due to internal refactoring, funFunLinkOffset
has been removed; use
getFunLink(Fun*)
instead.
Removed: int funFunLinkOffset;
We have enabled MSVC errors for comparisons between signed and unsigned values. If you encounter code which you cannot migrate for this (e.g. third-party libraries), add the following lines to your DLL's Makefile to disable the errors.
CFLAGS := $(filter-out /w34287,$(CFLAGS))
CFLAGS := $(filter-out /w34388,$(CFLAGS))
Time conversion functions throughout various parts of the system were incorrectly handling daylight-savings transitions. This has been fixed.
The setField
method used to permit almost any value type so long as
the type of the destination field was assignable to Object
. This is
a severe violation of the type system's rules and thus in the new
version, setField
ensures that the type of the instance passed in is
assignable to the type of the destination field.
Basically, the same rules that apply to normal field assignment
(instance.field = value
) now apply to setField
(instance.setField("field", value)
). There is an exception for
unboxing -- for example, you can use setField
to set an int
field,
even though you're passing in an Int
, since setField
takes an Object
.
public class Foo { public constructor() {} public str->str map_field; } { Foo f(); f.setField("map_field", new str->str()); // ok f.setField("map_field", new str->Object()); // forbidden in 14.0 }
When CM has a severe internal error, it will perform a "bail out". In some circumstances, the bail out may itself cause further errors. In 14.0, many of those situations are now handled by "fast fail" instead. If minidump generation or JIT debugging are configured, they will be invoked, otherwise the application will terminate ungracefully.
Some extremely severe errors (e.g. garbage collector state corruption) may go directly to "fast fail" without a "bail out" attempt, leaving no trace in log files.
This function is no longer prone to infinite loops and properly generates a string of characters, not their ASCII codes.
The internal printf wrapper function, which is also used for pln
and
pnn
, should now handle invalid characters by replacing them rather
than silently aborting output (and skipping the logfile).
The MemUsage tool has been refactored for better performance and significantly lower memory usage.
Executing code using C-M-SPC
(cm-compilation-interact-line
) in a
file with top-level run blocks used to cause problems due to using the
same namespace. This is fixed in 14.0.