Haxe 4.0 final field enforcement gap on dynamic targets
0 reputation · 23 Jul 2020, 13:48 UTC
0 reputation · 23 Jul 2020, 13:48 UTC
final keyword to provide compile-time immutability for class fields. While the type checker successfully prevents reassignment within Haxe source code, the enforcement of this constraint varies significantly depending on the target platform.On dynamic targets like JavaScript and PHP, the final modifier is erased during the compilation process. This results in generated properties that remain fully mutable and accessible via reflection, such as Reflect.setField. In contrast, static targets may optimize these fields into read-only storage or inlined constants.
There is currently an unresolved ambiguity regarding whether the compiler should inject runtime guards or specific metadata to protect these fields on dynamic platforms, or if the behavior is intended to remain strictly limited to static analysis. This creates a risk for developers relying on final for data integrity in browser environments.
Does the Haxe compiler intend to implement runtime immutability checks for final fields on dynamic backends? Is there a configuration option to prevent mutation via Reflection when a field is marked as final?
35275 reputation · 23 Jul 2020, 23:06 UTC
No. Haxe 4.0's final modifier is a compile-time check only. The compiler does not inject runtime immutability guards for dynamic backends such as JavaScript or PHP, and there is no standard configuration option that blocks Reflect.setField or equivalent reflection APIs for fields marked final.
Assuming Haxe 4.0.x, the following behavior is well established:
final field inside Haxe source code.final is erased during code generation; the emitted property remains writable.Reflect.setField can mutate a final field at runtime on dynamic targets.final fields.Whether a future Haxe release will add runtime enforcement is unsettled. Treat runtime immutability as absent unless you verify it against a specific compiler version and target.
Compile this minimal class to JavaScript:
class Foo {
public final bar: String;
public function new() {
bar = "initial";
}
}
Then run in the generated JavaScript context:
var foo = new Foo();
Reflect.setField(foo, "bar", "mutated");
trace(foo.bar); // prints "mutated" on JavaScript
If the value changes, the field is not runtime-immutable. Inspect the generated JavaScript to confirm there is no const, getter-only property, or frozen object.
Because the compiler does not enforce final at runtime on dynamic targets, enforce it in the emitted code or limit access to the instance:
Object.freeze after construction if the object shape is stable.final does not automatically map to PHP read-only properties.final as a security boundary in browser or server-side JavaScript; any script with access to the instance can mutate it.Missing diagnostic detail: which dynamic target are you shipping, JavaScript or PHP? The appropriate fallback differs by runtime, and knowing the target determines whether Object.freeze or a native read-only property is the right mitigation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.