Skip to content

Derived field initializers run before super(); Array#fill negative start corrupts memory; copyWithin traps when end < start #3064

Open

Description

@trackoor

Bug description

Three differences from TypeScript/JavaScript semantics inside the documented subset (same results with -O0 and -O3):

  1. Field initializers of a derived class with an explicit constructor run before super()

    In a subclass with an explicit constructor, field initializers are prepended to the constructor body (compiler.ts, makeFieldInitializationInConstructor before bodyStartIndex), so they run before the super(...) call and before the base class's own field initializers. Without an explicit subclass constructor the order is right.

  2. Array#fill(value, start) with start < -length writes before the buffer

    In FILL (std/assembly/util/bytes.ts), len has type usize (unsigned), so max(len + start, 0) does not clamp a negative sum to 0 when start < -<isize>len and the loop writes before dataStart, corrupting the heap.

  3. copyWithin(target, start, end) with end < start traps

    In Array#copyWithin (std/assembly/array.ts) and TypedArray#copyWithin (std/assembly/typedarray.ts), count = min(last - from, len - to) goes negative when last < from and is cast to usize for memory.copy, trapping with memory access out of bounds instead of being a no-op.

Steps to reproduce

// 1. Derived field initializer runs before super()
class A { a: i32 = 1; constructor(a: i32) { this.a = a; } }
class B extends A { b: i32 = this.a * 10; constructor() { super(5); } }
export function f(): i32 { return new B().b; } // returns 0, expected 50

// 2. Array#fill with start < -length corrupts heap memory before the buffer
export function g(): i32 {
  const a: i32[] = [1, 2, 3, 4];
  a.fill(7, -9); // expected [7, 7, 7, 7]
  __collect();
  return a[0];   // aborts: "null in ~lib/rt/itcms.ts:229:20"
}

// 3. copyWithin with end < start traps
export function h(): i32 {
  const a: i32[] = [1, 2, 3];
  a.copyWithin(1, 2, 1); // traps: "memory access out of bounds" (expected no-op)
  return a.length;
}

Compile with asc repro.ts -O0 (or -O3) and invoke f(), g(), and h().

Playground: https://www.assemblyscript.org/editor.html#IyFydW50aW1lPWluY3JlbWVudGFsCmNsYXNzIEEgeyBhOiBpMzIgPSAxOyBjb25zdHJ1Y3RvcihhOiBpMzIpIHsgdGhpcy5hID0gYTsgfSB9CmNsYXNzIEIgZXh0ZW5kcyBBIHsgYjogaTMyID0gdGhpcy5hICogMTA7IGNvbnN0cnVjdG9yKCkgeyBzdXBlcig1KTsgfSB9CmV4cG9ydCBmdW5jdGlvbiBmKCk6IGkzMiB7IHJldHVybiBuZXcgQigpLmI7IH0gLy8gZXhwZWN0ZWQgNTAKCmV4cG9ydCBmdW5jdGlvbiBnKCk6IGkzMiB7CiAgY29uc3QgYTogaTMyW10gPSBbMSwgMiwgMywgNF07CiAgYS5maWxsKDcsIC05KTsgLy8gZXhwZWN0ZWQgWzcsIDcsIDcsIDddCiAgX19jb2xsZWN0KCk7CiAgcmV0dXJuIGFbMF07Cn0KCmV4cG9ydCBmdW5jdGlvbiBoKCk6IGkzMiB7CiAgY29uc3QgYTogaTMyW10gPSBbMSwgMiwgM107CiAgYS5jb3B5V2l0aGluKDEsIDIsIDEpOyAvLyBleHBlY3RlZCBuby1vcAogIHJldHVybiBhLmxlbmd0aDsKfQojIWh0bWwKPHRleHRhcmVhIGlkPSJvdXRwdXQiIHN0eWxlPSJoZWlnaHQ6IDEwMCU7IHdpZHRoOiAxMDAlIiByZWFkb25seT48L3RleHRhcmVhPgo8c2NyaXB0PgooYXN5bmMgKCkgPT4gewogIGNvbnN0IG91dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJvdXRwdXQiKTsKICBmb3IgKGNvbnN0IG4gb2YgWyJmIiwgImgiLCAiZyJdKSB7CiAgICBjb25zdCBleHBvcnRzID0gYXdhaXQgaW5zdGFudGlhdGUoYXdhaXQgY29tcGlsZSgpLCB7fSk7CiAgICB0cnkgeyBvdXQudmFsdWUgKz0gbiArICIgPSAiICsgZXhwb3J0c1tuXSgpICsgIlxuIjsgfSBjYXRjaCAoZSkgeyBvdXQudmFsdWUgKz0gbiArICIgdGhyZXcgIiArIGUubWVzc2FnZSArICJcbiI7IH0KICB9Cn0pKCk7Cjwvc2NyaXB0Pgo=

AssemblyScript version

v0.28.20 (main b6bda05)

Credit: https://ucsbplse.github.io/

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions