Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,110 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
if (this.Worker) {
(function TestWorkerTerminated() {
let workerLockScript = `onmessage = function({data:msg}) {
let {mutex, sharedObj} = msg;
Atomics.Mutex.lock(mutex, function() {
postMessage('Lock acquired');
while(!Atomics.load(sharedObj, 'done')) {}
});
postMessage('Lock released');
};
postMessage('Worker started');`;
let workerWaitScript = `onmessage = function({data:msg}) {
let {cv_mutex, cv, shared_Obj} = msg;
Atomics.Mutex.lock(cv_mutex, function() {
postMessage('Waiting started');
Atomics.Condition.wait(cv, cv_mutex);
});
postMessage('Waiting done');
};
postMessage('Worker started');`;
let workerAsyncScript = `onmessage = function({data:msg}) {
if (msg.type === 'lock') {
let {mutex, sharedObj} = msg;
for (let i = 0; i < 10; i++) {
Atomics.Mutex.lockAsync(mutex, async function() {})
}
postMessage('Lock waiters queued');
}
else if (msg.type === 'wait'){
let {cv_mutex, cv} = msg;
for (let i = 0; i < 10; i++) {
Atomics.Mutex.lockAsync(cv_mutex, async function() {
await Atomics.Condition.waitAsync(cv, cv_mutex);
})
}
// The waiters will be processed when the microtask queue is flushed
// after this task. This will queue both tasks and microtasks, so other
// incoming tasks might be handled before the the waitAsyncs are
// executed.
postMessage('Handled wait messages');
}
else {
postMessage(%AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
}
};
postMessage('Worker started');`;
let workerLock1 = new Worker(workerLockScript, {type: 'string'});
assertEquals('Worker started', workerLock1.getMessage());
let workerLock2 = new Worker(workerLockScript, {type: 'string'});
assertEquals('Worker started', workerLock2.getMessage());
let workerWait1 = new Worker(workerWaitScript, {type: 'string'});
assertEquals('Worker started', workerWait1.getMessage());
let workerWait2 = new Worker(workerWaitScript, {type: 'string'});
assertEquals('Worker started', workerWait2.getMessage());
let workerAsync = new Worker(workerAsyncScript, {type: 'string'});
assertEquals('Worker started', workerAsync.getMessage());
let SharedType = new SharedStructType(['done']);
let sharedObj = new SharedType();
sharedObj.done = false;
let mutex = new Atomics.Mutex;
let cv_mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
let lock_msg = {mutex, sharedObj, type: 'lock'};
let wait_msg = {cv_mutex, cv, type: 'wait'};
let count_msg = {type: 'count'};
workerLock1.postMessage(lock_msg);
workerWait1.postMessage(wait_msg);
assertEquals('Lock acquired', workerLock1.getMessage());
assertEquals('Waiting started', workerWait1.getMessage());
workerAsync.postMessage(lock_msg);
assertEquals('Lock waiters queued', workerAsync.getMessage());
workerAsync.postMessage(wait_msg);
assertEquals('Handled wait messages', workerAsync.getMessage());
// Wait until the waitAsync-related tasks are processed in the worker before
// posting a new message.
while(%AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv) !== 11) {
}
// Verify there are 30 waiters in the async waiter list of the workerAsync's
// isolate.
workerAsync.postMessage(count_msg);
assertEquals(30, workerAsync.getMessage());
workerLock2.postMessage(lock_msg);
workerWait2.postMessage(wait_msg);
assertEquals('Waiting started', workerWait2.getMessage());
while(%AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv) !== 12) {
}
workerAsync.terminate();
while(!Atomics.Mutex.tryLock(cv_mutex, function() {
sharedObj.done = true;
Atomics.Condition.notify(cv, 20);
}).success) {
}
assertEquals('Lock released', workerLock1.getMessage());
assertEquals('Lock acquired', workerLock2.getMessage());
assertEquals('Lock released', workerLock2.getMessage());
assertEquals('Waiting done', workerWait1.getMessage());
assertEquals('Waiting done', workerWait2.getMessage());
workerLock1.terminate();
workerLock2.terminate();
workerWait1.terminate();
workerWait2.terminate();
})();
}

View File

@ -0,0 +1,61 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
function TestBrandChecks(struct1, struct2, sa, mutex, cv) {
assertTrue(SharedStructType.isSharedStruct(struct1));
assertTrue(SharedStructType.isSharedStruct(struct2));
for (const x of [sa, mutex, cv]) {
assertFalse(SharedStructType.isSharedStruct(x));
}
assertTrue(SharedArray.isSharedArray(sa));
assertTrue(sa instanceof SharedArray);
for (const x of [struct1, struct2, mutex, cv]) {
assertFalse(SharedArray.isSharedArray(x));
}
assertTrue(Atomics.Mutex.isMutex(mutex));
assertTrue(mutex instanceof Atomics.Mutex);
for (const x of [struct1, struct2, sa, cv]) {
assertFalse(Atomics.Mutex.isMutex(x));
}
assertTrue(Atomics.Condition.isCondition(cv));
assertTrue(cv instanceof Atomics.Condition);
for (const x of [struct1, struct2, sa, mutex]) {
assertFalse(Atomics.Condition.isCondition(x));
}
const falsehoods = [42, -0, undefined, null, true, false, 'foo', {}, []];
for (const x of falsehoods) {
assertFalse(SharedStructType.isSharedStruct(x));
assertFalse(SharedArray.isSharedArray(x));
assertFalse(Atomics.Mutex.isMutex(x));
assertFalse(Atomics.Condition.isCondition(x));
}
}
// Test same Realm.
const S1 = new SharedStructType(['field']);
const S2 = new SharedStructType(['field2']);
const struct1 = new S1();
const struct2 = new S2();
const sa = new SharedArray(1);
const mutex = new Atomics.Mutex();
const cv = new Atomics.Condition();
TestBrandChecks(struct1, struct2, sa, mutex, cv);
// Test cross-Realm.
const [otherStruct1, otherStruct2, otherSa, otherMutex, otherCv] =
Realm.eval(Realm.create(), `const S1 = new SharedStructType(['field']);
const S2 = new SharedStructType(['field2']);
const struct1 = new S1();
const struct2 = new S2();
const sa = new SharedArray(1);
const mutex = new Atomics.Mutex();
const cv = new Atomics.Condition();
[struct1, struct2, sa, mutex, cv]`);
TestBrandChecks(otherStruct1, otherStruct2, otherSa, otherMutex, otherCv);

View File

@ -0,0 +1,40 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
// Shared objects have fixed layout and cannot have their properties
// redefined. They are constructed sealed. In ordinary objects, sealed objects
// can be redefined to be frozen. This is disallowed for shared objects.
function TestSharedObjectCannotRedefineProperty(sharedObj) {
function TestRedefine(sharedObj, propName) {
if (Object.hasOwn(sharedObj, propName)) {
assertThrows(() => Object.defineProperty(sharedObj, propName, {
value: 99,
enumerable: true,
writable: false,
configurable: false
}));
Object.defineProperty(
sharedObj, propName,
{value: 99, enumerable: true, writable: true, configurable: false});
assertEquals(99, sharedObj[propName]);
}
}
TestRedefine(sharedObj, 'p');
TestRedefine(sharedObj, '0');
// Objects without any properties are a degenerate case and are considered
// frozen (or any integrity level, really).
if (Object.getOwnPropertyNames(sharedObj).length > 0) {
assertThrows(() => Object.freeze(sharedObj));
}
assertTrue(Object.isSealed(sharedObj));
}
TestSharedObjectCannotRedefineProperty(new Atomics.Condition());
TestSharedObjectCannotRedefineProperty(new Atomics.Mutex());
TestSharedObjectCannotRedefineProperty(new (new SharedStructType(['p'])));
TestSharedObjectCannotRedefineProperty(new SharedArray(1));

View File

@ -0,0 +1,7 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --allow-natives-syntax --expose-gc
gc();

View File

@ -0,0 +1,27 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
(function TestAsyncWaitPromisePrototypeTampering() {
const mutex = new Atomics.Mutex;
const cv = new Atomics.Condition;
let finishedExecution = false;
let lockAsyncPromise = Atomics.Mutex.lockAsync(mutex, async () => {
Atomics.Condition.waitAsync(cv, mutex);
finishedExecution = true;
});
let thenExecuted = false;
lockAsyncPromise.then(() => {
thenExecuted = true;
});
// Run Microtasks
setTimeout(() => {
assertTrue(finishedExecution);
assertTrue(thenExecuted);
assertEquals(1, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv));
}, 0);
})();

View File

@ -0,0 +1,99 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax --expose-gc
// Flags: --no-lazy-compile-dispatcher --no-concurrent-recompilation
if (this.Worker) {
(function TestRealmWithAsyncWaiterDisposed() {
let workerScript = `onmessage = function({data:msg}) {
let {mutex, cv, sharedObj} = msg;
Atomics.Mutex.lock(mutex, function() {
postMessage('Lock acquired');
Atomics.Condition.wait(cv, mutex);
sharedObj.counter++;
});
postMessage('Stopped waiting');
};
postMessage('started');`;
let realmScript = `
Atomics.Mutex.lockAsync(Realm.shared.mutex, async function() {
await Atomics.Condition.waitAsync(Realm.shared.cv, Realm.shared.mutex);
Realm.shared.sharedObj.counter++;
});`;
let worker1 = new Worker(workerScript, {type: 'string'});
assertEquals('started', worker1.getMessage());
let worker2 = new Worker(workerScript, {type: 'string'});
assertEquals('started', worker2.getMessage());
let realm = Realm.create();
let mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
let SharedType = new SharedStructType(['counter']);
let sharedObj = new SharedType();
sharedObj.counter = 0;
let lock_msg = {mutex, cv, sharedObj};
Realm.shared = {mutex, cv, sharedObj};
worker1.postMessage(lock_msg);
assertEquals('Lock acquired', worker1.getMessage())
// Wait until the cv waiter is queued.
while (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv) !== 1) {
}
Realm.eval(realm, realmScript);
// Flush microtask queue, the async lock is taken and waitAsync is called.
setTimeout(() => {
assertEquals(
2, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv));
worker2.postMessage(lock_msg);
assertEquals('Lock acquired', worker2.getMessage());
// Wait until the cv waiter is queued.
while (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv) !== 3) {
}
Realm.dispose(realm);
// Cleanup the realm's native context.
let collected = false;
gc({type: 'major', execution: 'async'})
.then(() => {collected = true}, (e) => {
throw e;
});
let executeAsserts = () => {
// The realm was disposed but the waiter nodes created for both: the
// asyncLock and the asyncWait are still in the isolate's async waiter
// list.
assertEquals(
2, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
// There are still 3 waiters in the cv queue.
assertEquals(
3, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv));
assertEquals(0, sharedObj.counter);
Atomics.Condition.notify(cv, 3);
setTimeout(() => {
assertEquals('Stopped waiting', worker1.getMessage());
assertEquals('Stopped waiting', worker2.getMessage());
assertEquals(
0, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv));
// The notification for the async waiter was processed but execution
// didn't continue.
assertEquals(2, sharedObj.counter);
}, 0);
};
let asyncLoop = () => {
if (!collected) {
setTimeout(asyncLoop, 0);
return;
}
executeAsserts();
};
asyncLoop();
}, 0);
})();
}

View File

@ -0,0 +1,30 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
(function TestAsyncWaitTimeout() {
let mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
let notified = false;
Atomics.Mutex.lockAsync(mutex, async function() {
await Atomics.Condition.waitAsync(cv, mutex, 1);
notified = true;
});
assertEquals(false, notified);
// The wait will timeout after 1 ms, but setTimeout in tests doesn't wait
// for the timeout if the test is already done, so use an async loop to wait.
let asyncLoop = () => {
if (!notified) {
setTimeout(asyncLoop, 0);
return;
}
// The waiter timedout and was removed from this isolate's async waiters
// list.
assertEquals(0, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
};
asyncLoop();
})();

View File

@ -0,0 +1,47 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax --expose-gc
(function TestAsyncWait() {
let mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
let notified = false;
let locked = Atomics.Mutex.lockAsync(mutex, async function() {
await Atomics.Condition.waitAsync(cv, mutex);
// There is only one waiter in this isolate's async waiter list, even though
// the lock has been taked 2 times (once in the lockAsync and once after the
// wait was notified).
assertEquals(1, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
notified = true;
});
// The lock is held, but the callback is not executed until the next
// microtask.
assertFalse(Atomics.Mutex.tryLock(mutex, function() {}).success);
// Call gc to test that everything is kept alive while the lock is taken.
gc();
setTimeout(() => {
// The lock callback has been executed and the condition variable
// queued one waiter, and the lock is released.
assertEquals(1, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv));
// The condition variable waiters was added to this isolate's async waiter
// list and the mutex waiter is still in the list.
assertEquals(2, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
assertTrue(Atomics.Mutex.tryLock(mutex, function() {}).success);
Atomics.Condition.notify(cv, 1);
// The notification task has been posted, but the execution will continue
// in the next microtask.
assertFalse(notified);
setTimeout(() => {
assertTrue(notified);
// The waiter was removed from this isolate's async waiters list.
assertEquals(
0, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
}, 0);
}, 0);
})();

View File

@ -0,0 +1,43 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
"use strict";
if (this.Worker) {
(function TestWait() {
let workerScript =
`onmessage = function({data:msg}) {
let mutex = msg.mutex;
let cv = msg.cv;
let res = Atomics.Mutex.lock(mutex, function() {
return Atomics.Condition.wait(cv, mutex);
});
postMessage(res);
};`;
let mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
let msg = {mutex, cv};
let worker1 = new Worker(workerScript, { type: 'string' });
let worker2 = new Worker(workerScript, { type: 'string' });
worker1.postMessage(msg);
worker2.postMessage(msg);
// Spin until both workers are waiting.
while (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv) != 2) {}
assertEquals(2, Atomics.Condition.notify(cv, 2));
assertEquals(true, worker1.getMessage());
assertEquals(true, worker2.getMessage());
worker1.terminate();
worker2.terminate();
})();
}

View File

@ -0,0 +1,43 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
let mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
(function TestConditionWaitNotAllowed() {
assertThrows(() => {
Atomics.Mutex.lock(mutex, () => {
%SetAllowAtomicsWait(false);
Atomics.Condition.wait(cv, mutex);
});
});
%SetAllowAtomicsWait(true);
})();
(function TestConditionMutexNotHeld() {
// Cannot wait on a mutex not owned by the current thread.
assertThrows(() => {
Atomics.Condition.wait(cv, mutex);
});
})();
(function TestConditionNoWaiters() {
// Notify returns number of threads woken up.
assertEquals(0, Atomics.Condition.notify(cv));
})();
(function TestConditionWaitTimeout() {
Atomics.Mutex.lock(mutex, () => {
assertEquals(false, Atomics.Condition.wait(cv, mutex, 100));
});
})();
// Mutexes can be assigned to shared objects.
(function TestConditionCanBeAssignedToSharedObjects() {
const Box = new SharedStructType(["payload"]);
const box = new Box;
box.payload = cv;
})();

View File

@ -0,0 +1,34 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
(function TestAsyncLock() {
let mutex = new Atomics.Mutex;
let lockPromise = Atomics.Mutex.lockAsync(mutex, function() {
throw new Error('Callback thrown');
})
let rejectExecuted = false;
let lockWasReleased = false;
let numAsyncWaitersInRejectHandler = 0;
lockPromise.then(
(result) => {
assertUnreachable();
},
(error) => {
lockWasReleased = Atomics.Mutex.tryLock(mutex, function() {}).success;
numAsyncWaitersInRejectHandler =
%AtomicsSychronizationNumAsyncWaitersInIsolateForTesting();
rejectExecuted = true;
assertEquals('Callback thrown', error.message);
});
// The lock and then callbacks will be executed when the microtask queue
// is flushed, before proceeding to the next task.
setTimeout(() => {
assertTrue(rejectExecuted);
assertTrue(rejectExecuted);
assertEquals(0, numAsyncWaitersInRejectHandler);
}, 0);
})();

View File

@ -0,0 +1,38 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
(function TestAsyncPromiseResolveValueAndChain() {
let mutex = new Atomics.Mutex();
let thenExecuted = false;
let waitResolve;
let waitPromise = new Promise((resolve) => {
waitResolve = resolve;
});
// If the return value of the callback is a promise, then lockPromise will
// be resolved when the promise is resolved.
let lockPromise = Atomics.Mutex.lockAsync(mutex, async function() {
return waitPromise;
});
assertFalse(Atomics.Mutex.tryLock(mutex, function() {}).success);
waitResolve(42);
lockPromise.then((result) => {
assertTrue(Atomics.Mutex.tryLock(mutex, function() {}).success);
assertEquals(0, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
// lockPromise is resolved with the same value as the callback promise.
thenExecuted = true;
assertEquals(42, result.value);
assertTrue(result.success);
});
// The lock and then callbacks will be executed when the microtask queue is
// flushed, before proceeding to the next task.
setTimeout(() => {
assertTrue(thenExecuted);
}, 0);
})();

View File

@ -0,0 +1,89 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax --expose-gc
// Flags: --no-lazy-compile-dispatcher --no-concurrent-recompilation
if (this.Worker) {
(function TestRealmWithAsyncWaiterDisposed() {
let workerLockScript = `onmessage = function({data:msg}) {
let {mutex, sharedObj} = msg;
Atomics.Mutex.lock(mutex, function() {
postMessage('Lock acquired');
while (!Atomics.load(sharedObj, 'done')) {
}
});
postMessage('Lock released');
};
postMessage('started');`;
let realmLockAsyncScript = `
Atomics.Mutex.lockAsync(Realm.shared.mutex, async function() {
Realm.shared.realmLocked = true;
});`;
let workerLock1 = new Worker(workerLockScript, {type: 'string'});
assertEquals('started', workerLock1.getMessage());
let workerLock2 = new Worker(workerLockScript, {type: 'string'});
assertEquals('started', workerLock2.getMessage());
let realmLockAsyc = Realm.create();
let mutex = new Atomics.Mutex;
let sharedObj = new (new SharedStructType(['done']))();
sharedObj.done = false;
let lock_msg = {mutex, sharedObj};
workerLock1.postMessage(lock_msg);
assertEquals('Lock acquired', workerLock1.getMessage());
Realm.shared = {mutex, realmLocked: false};
Realm.eval(realmLockAsyc, realmLockAsyncScript);
workerLock2.postMessage(lock_msg);
while (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex) !== 2) {
}
Realm.dispose(realmLockAsyc);
let collected = false;
// Cleanup the realm's native context.
gc({type: 'major', execution: 'async'})
.then(() => {collected = true}, (e) => {
throw e;
});
let executeAsserts = () => {
// The realm was disposed but the waiter node that it created is still in
// the isolate's unlocked waiters list.
assertEquals(
1, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
// There are still 2 waiters in the mutex queue.
assertEquals(
2, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex));
sharedObj.done = true;
assertEquals('Lock released', workerLock1.getMessage());
// If the async waiter was still alive, it would acquire the lock and
// set Realm.shared.realmLocked to true when we flush the microtask queue
// here.
setTimeout(() => {
// The lock was never acquired by the realm because it was destroyed.
assertFalse(Realm.shared.realmLocked)
// The async waiter notification was processed, removed itself from this
// isolate's async waiters list and notified the next worker.
assertEquals(
0, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting())
assertEquals('Lock acquired', workerLock2.getMessage());
assertEquals('Lock released', workerLock2.getMessage());
}, 0);
};
let asyncLoop = () => {
if (!collected) {
setTimeout(asyncLoop, 0);
return;
}
executeAsserts();
};
asyncLoop();
})();
}

View File

@ -0,0 +1,68 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
'use strict';
if (this.Worker) {
(function TestTimeout() {
let workerScript = `onmessage = function({data:msg}) {
let {mutex, box, cv, cv_mutex} = msg;
Atomics.Mutex.lock(mutex, function() {
Atomics.Mutex.lock(cv_mutex, function() {
postMessage("lock acquired");
while(!box.timedOut) {
Atomics.Condition.wait(cv, cv_mutex);
}
});
});
postMessage("done");
};
postMessage("started");`;
let worker = new Worker(workerScript, {type: 'string'});
assertEquals('started', worker.getMessage());
let Box = new SharedStructType(['timedOut']);
let box = new Box();
box.timedOut = false;
let mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
let cv_mutex = new Atomics.Mutex;
let msg = {mutex, box, cv, cv_mutex};
worker.postMessage(msg);
assertEquals('lock acquired', worker.getMessage());
let thenResolved = false;
let lockPromise = Atomics.Mutex.lockAsync(mutex, () => {
assertUnreachable();
}, 1);
lockPromise.then((result) => {
thenResolved = true;
assertEquals(false, result.success);
assertEquals(undefined, result.value);
assertEquals(
0, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
});
let asyncLoop = () => {
if (!thenResolved) {
setTimeout(asyncLoop, 0);
} else {
// Ensure that the worker is waiting on the condition variable before
// notifying it.
while(!Atomics.Mutex.tryLock(cv_mutex, function() {
box.timedOut = true;
Atomics.Condition.notify(cv, 1);
}).success) {
}
assertEquals('done', worker.getMessage());
worker.terminate();
}
};
asyncLoop();
})();
}

View File

@ -0,0 +1,64 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
'use strict';
if (this.Worker) {
(function TestTimeout() {
let workerScript = `onmessage = function({data:msg}) {
let {mutex, box, cv, cv_mutex} = msg;
Atomics.Mutex.lock(mutex, function() {
Atomics.Mutex.lock(cv_mutex, function() {
// Atomics.Condition.wait releases cv_mutex but not mutex.
postMessage("lock acquired");
Atomics.Condition.wait(cv, cv_mutex);
});
});
postMessage("done");
};
postMessage("started");`;
let worker = new Worker(workerScript, {type: 'string'});
assertEquals('started', worker.getMessage());
let mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
let cv_mutex = new Atomics.Mutex;
let msg = {mutex, cv, cv_mutex};
worker.postMessage(msg);
assertEquals('lock acquired', worker.getMessage());
assertFalse(Atomics.Mutex.tryLock(mutex, function() {}).success);
let thenResolved = false;
let lockPromise = Atomics.Mutex.lockAsync(mutex, () => {
return 42;
});
// There is one waiter for mutex in this isolate's async waiter list.
assertEquals(1, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
lockPromise.then((result) => {
thenResolved = true;
assertEquals(true, result.success);
assertEquals(42, result.value);
});
let cv_promise = Atomics.Mutex.lockAsync(cv_mutex, () => {
Atomics.Condition.notify(cv, 1);
});
cv_promise.then((result) => {
assertEquals(true, result.success);
assertEquals('done', worker.getMessage());
});
let asyncLoop = () => {
if (!thenResolved) {
setTimeout(asyncLoop, 0);
}
};
asyncLoop();
})();
}

View File

@ -0,0 +1,32 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
(function TestAsyncLock() {
let mutex = new Atomics.Mutex;
let lockPromise = Atomics.Mutex.lockAsync(mutex, function() {
return 42;
})
// Lock is taken but the callback won't be executed until the next microtask.
assertFalse(Atomics.Mutex.tryLock(mutex, function() {}).success);
assertEquals(1, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
let resolverExecuted = false;
lockPromise.then((result) => {
// The lock was released before the then callback is executed.
assertTrue(Atomics.Mutex.tryLock(mutex, function() {}).success);
// Ensure the promise's result value property is set to the return
// value of the callback.
assertEquals(42, result.value);
assertEquals(true, result.success);
resolverExecuted = true;
});
setTimeout(() => {
// The lock and then callbacks will be executed when the microtask queue
// is flushed, before proceeding to the next task.
assertEquals(resolverExecuted, true);
assertEquals(0, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
}, 0);
})();

View File

@ -0,0 +1,52 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
if (this.Worker) {
(function TestAsyncLockedWorkerTerminated() {
let workerAsyncScript = `onmessage = function({data:msg}) {
let {mutex, sharedObj} = msg;
let asyncLoop = (resolve) => {
if (!Atomics.load(sharedObj, 'done')) {
setTimeout(asyncLoop, 0);
}
else {
resolve();
}
};
Atomics.Mutex.lockAsync(mutex, async function() {
postMessage('Lock acquired');
await new Promise((resolve) => {asyncLoop(resolve);});
});
};`;
let workerLockScript = `onmessage = function({data:msg}) {
let {mutex} = msg;
Atomics.Mutex.lock(mutex, function() {
postMessage('Lock acquired');
});
postMessage('Lock released');
};`;
let workerAsync = new Worker(workerAsyncScript, {type: 'string'});
let workerLock = new Worker(workerLockScript, {type: 'string'});
let mutex = new Atomics.Mutex;
let SharedType = new SharedStructType(['done']);
let sharedObj = new SharedType();
sharedObj.done = false;
workerAsync.postMessage({mutex, sharedObj});
assertEquals('Lock acquired', workerAsync.getMessage());
assertEquals(
0, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex));
workerLock.postMessage({mutex});
while (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex) !== 1) {
}
workerAsync.terminate();
assertEquals('Lock acquired', workerLock.getMessage());
assertEquals('Lock released', workerLock.getMessage());
workerLock.terminate();
})();
}

View File

@ -0,0 +1,62 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
if (this.Worker) {
(function TestAsyncLockedWorkerTerminated() {
let workerAsyncScript = `onmessage = function({data:msg}) {
let {mutex, sharedObj} = msg;
let asyncLoop = (resolve) => {
if (!Atomics.load(sharedObj, 'done')) {
setTimeout(asyncLoop, 0);
}
else {
resolve();
}
};
Atomics.Mutex.lockAsync(mutex, async function() {
postMessage('Lock acquired');
await new Promise((resolve) => {asyncLoop(resolve);});
});
};`;
let workerLockScript = `onmessage = function({data:msg}) {
let {mutex, sharedObj} = msg;
Atomics.Mutex.lock(mutex, function() {
postMessage('Lock acquired');
while(!Atomics.load(sharedObj, 'done')) {}
});
postMessage('Lock released');
};`;
let workerAsync = new Worker(workerAsyncScript, {type: 'string'});
let workerLock1 = new Worker(workerLockScript, {type: 'string'});
let workerLock2 = new Worker(workerLockScript, {type: 'string'});
let mutex = new Atomics.Mutex;
let SharedType = new SharedStructType(['done']);
let sharedObj = new SharedType();
sharedObj.done = false;
let msg = {mutex, sharedObj};
workerLock1.postMessage(msg);
assertEquals(workerLock1.getMessage(), 'Lock acquired');
assertEquals(
0, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex));
workerAsync.postMessage(msg);
while (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex) !== 1) {
}
workerLock2.postMessage(msg);
while (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex) !== 2) {
}
workerAsync.terminate();
sharedObj.done = true;
assertEquals(workerLock1.getMessage(), 'Lock released');
assertEquals(workerLock2.getMessage(), 'Lock acquired');
assertEquals(workerLock2.getMessage(), 'Lock released');
workerLock1.terminate();
workerLock2.terminate();
})();
}

View File

@ -0,0 +1,19 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --harmony-struct
let mtx = new Atomics.Mutex();
let cnd = new Atomics.Condition();
Atomics.Mutex.lock(mtx, () => {
Atomics.Condition.wait(cnd, mtx, 100);
});
gc();
gc();
Atomics.Mutex.lock(mtx, () => {
Atomics.Condition.wait(cnd, mtx, 100);
});

View File

@ -0,0 +1,175 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
"use strict";
if (this.Worker) {
(function TestMutexWorkers() {
let workerScript =
`onmessage = function({data:msg}) {
let mutex = msg.mutex;
let box = msg.box;
for (let i = 0; i < 10; i++) {
Atomics.Mutex.lock(mutex, function() {
box.counter++;
});
}
postMessage("done");
};
postMessage("started");`;
let worker1 = new Worker(workerScript, { type: 'string' });
let worker2 = new Worker(workerScript, { type: 'string' });
assertEquals("started", worker1.getMessage());
assertEquals("started", worker2.getMessage());
let Box = new SharedStructType(['counter']);
let box = new Box();
box.counter = 0;
let mutex = new Atomics.Mutex();
let msg = { mutex, box };
worker1.postMessage(msg);
worker2.postMessage(msg);
assertEquals("done", worker1.getMessage());
assertEquals("done", worker2.getMessage());
assertEquals(20, box.counter);
worker1.terminate();
worker2.terminate();
})();
(function TestMutexWaiters() {
let workerScript = `onmessage = function({data:msg}) {
let mutex = msg.mutex;
let box = msg.box;
Atomics.Mutex.lock(mutex, function() {
while(!Atomics.load(box, 'isDone')) {}
});
postMessage("done");
};
postMessage("started");`;
let worker1 = new Worker(workerScript, {type: 'string'});
let worker2 = new Worker(workerScript, {type: 'string'});
let worker3 = new Worker(workerScript, {type: 'string'});
assertEquals('started', worker1.getMessage());
assertEquals('started', worker2.getMessage());
assertEquals('started', worker3.getMessage());
let Box = new SharedStructType(['isDone']);
let box = new Box();
box.isDone = false;
let mutex = new Atomics.Mutex();
let msg = {mutex, box};
worker1.postMessage(msg);
worker2.postMessage(msg);
worker3.postMessage(msg);
while (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex) !== 2) {}
box.isDone = true;
assertEquals('done', worker1.getMessage());
assertEquals('done', worker2.getMessage());
assertEquals('done', worker3.getMessage());
assertEquals(0, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex));
worker1.terminate();
worker2.terminate();
worker3.terminate();
})();
(function TestTimeout() {
let worker1Script = `onmessage = function({data:msg}) {
let {mutex, box, cv, cv_mutex} = msg;
Atomics.Mutex.lock(mutex, function() {
Atomics.Mutex.lock(cv_mutex, function() {
// Post inside cv_mutex critical section to ensure worker2
// requests it after worker1.
postMessage("lock acquired");
while(!box.timedOut) {
Atomics.Condition.wait(cv, cv_mutex);
}
});
});
postMessage("done");
};
postMessage("started");`;
let worker2Script = `onmessage = function({data:msg}) {
let {mutex, box, cv, cv_mutex} = msg;
let result =
Atomics.Mutex.lockWithTimeout(mutex, ()=>{} , 1);
box.timedOut = !result.success;
// Use cv_mutex to ensure the notify happens after the wait.
Atomics.Mutex.lock(cv_mutex, function() {
Atomics.Condition.notify(cv);
});
postMessage("done");
};
postMessage("started");`;
let worker1 = new Worker(worker1Script, {type: 'string'});
let worker2 = new Worker(worker2Script, {type: 'string'});
assertEquals('started', worker2.getMessage());
assertEquals('started', worker1.getMessage());
let Box = new SharedStructType(['timedOut']);
let box = new Box();
box.timedOut = false;
let mutex = new Atomics.Mutex;
let cv = new Atomics.Condition;
let cv_mutex = new Atomics.Mutex;
let msg = {mutex, box, cv, cv_mutex};
worker1.postMessage(msg);
assertEquals('lock acquired', worker1.getMessage());
worker2.postMessage(msg);
assertEquals('done', worker2.getMessage());
assertEquals('done', worker1.getMessage());
worker1.terminate();
worker2.terminate();
})();
(function TestUnlockWhileTimingOut() {
// Racy version of the timeout test, where the worker holding the lock might
// unlock it while the other worker is timing out and cleaning its waiter from
// the queue.
let workerLockScript = `onmessage = function({data:msg}) {
let {mutex, box} = msg;
Atomics.Mutex.lock(mutex, function() {
while (!Atomics.load(box, 'isDone')) {}
});
postMessage("done");
}
postMessage("started");`;
let timedOutWorkerScript = `onmessage = function({data:msg}) {
let {mutex} = msg;
let result = Atomics.Mutex.lockWithTimeout(mutex, function() {}, 0);
postMessage("done");
}
postMessage("started");`;
let workerLock = new Worker(workerLockScript, {type: 'string'});
let timedOutWorker = new Worker(timedOutWorkerScript, {type: 'string'});
assertEquals('started', workerLock.getMessage());
assertEquals('started', timedOutWorker.getMessage());
let Box = new SharedStructType(['isDone']);
let box = new Box();
box.isDone = false;
let mutex = new Atomics.Mutex;
workerLock.postMessage({mutex, box});
timedOutWorker.postMessage({mutex});
box.isDone = true;
assertEquals('done', workerLock.getMessage());
assertEquals('done', timedOutWorker.getMessage());
assertEquals(0, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex));
assertTrue(Atomics.Mutex.tryLock(mutex, function() {}).success);
workerLock.terminate();
timedOutWorker.terminate();
})();
}

View File

@ -0,0 +1,76 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
let mutex = new Atomics.Mutex;
let locked_count = 0;
assertEquals(42, Atomics.Mutex.lock(mutex, () => {
locked_count++; return 42;
}));
assertEquals(locked_count, 1);
let lockResult = Atomics.Mutex.tryLock(mutex, () => {
locked_count++;
});
assertEquals(true, lockResult.success);
assertEquals(locked_count, 2);
// Recursively locking throws.
Atomics.Mutex.lock(mutex, () => {
locked_count++;
assertThrows(() => {
Atomics.Mutex.lock(mutex, () => { throw "unreachable"; });
}, Error);
});
assertEquals(locked_count, 3);
// Recursive tryLocking returns returns false success value.
Atomics.Mutex.lock(mutex, () => {
locked_count++;
lockResult = Atomics.Mutex.tryLock(mutex, () => {
throw 'unreachable';
});
assertEquals(false, lockResult.success);
assertEquals(undefined, lockResult.value);
});
assertEquals(locked_count, 4);
// Throwing in the callback should unlock the mutex.
assertThrowsEquals(() => {
Atomics.Mutex.lock(mutex, () => { throw 42; });
}, 42);
Atomics.Mutex.lock(mutex, () => { locked_count++; });
assertEquals(locked_count, 5);
assertThrowsEquals(() => {
Atomics.Mutex.tryLock(mutex, () => { throw 42; });
}, 42);
Atomics.Mutex.tryLock(mutex, () => { locked_count++; });
assertEquals(locked_count, 6);
// Mutexes can be assigned to shared objects.
(function TestMutexCanBeAssignedToSharedObjects() {
const Box = new SharedStructType(["payload"]);
const box = new Box;
box.payload = mutex;
})();
// lockWithTimeout's return value is an object with a value field
// and a success field.
lockResult = Atomics.Mutex.lockWithTimeout(mutex, () => 42, 1);
assertEquals(42, lockResult.value);
assertEquals(true, lockResult.success);
// Timeout must be a number.
assertThrows(() => {
Atomics.Mutex.lockWithTimeout(mutex, () => {}, '42');
})
assertThrows(() => {
Atomics.Mutex.lockWithTimeout(mutex, () => {}, {});
})
assertThrows(() => {
Atomics.Mutex.lockWithTimeout(mutex, () => {});
})

View File

@ -0,0 +1,42 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
function Foo() {}
function TestInstanceOfMutex() {
return Foo instanceof Atomics.Mutex;
}
function TestInstanceOfCondition() {
return Foo instanceof Atomics.Condition;
}
function TestInstanceOfSharedArray() {
return Foo instanceof SharedArray;
}
function TestInstanceOfSharedStruct() {
return Foo instanceof (new SharedStructType(["foo"]));
}
%PrepareFunctionForOptimization(TestInstanceOfMutex);
%PrepareFunctionForOptimization(TestInstanceOfCondition);
%PrepareFunctionForOptimization(TestInstanceOfSharedArray);
%PrepareFunctionForOptimization(TestInstanceOfSharedStruct);
for (let i = 0; i < 10; i++) {
assertFalse(TestInstanceOfMutex());
assertFalse(TestInstanceOfCondition());
assertFalse(TestInstanceOfSharedArray());
assertFalse(TestInstanceOfSharedStruct());
}
%OptimizeFunctionOnNextCall(TestInstanceOfMutex);
%OptimizeFunctionOnNextCall(TestInstanceOfCondition);
%OptimizeFunctionOnNextCall(TestInstanceOfSharedArray);
%OptimizeFunctionOnNextCall(TestInstanceOfSharedStruct);
assertFalse(TestInstanceOfMutex());
assertFalse(TestInstanceOfCondition());
assertFalse(TestInstanceOfSharedArray());
assertFalse(TestInstanceOfSharedStruct());

View File

@ -0,0 +1,26 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
// Private brands from private methods should not be able to extend shared
// objects. This is unlike ordinary objects, which can get private fields even
// if sealed/frozen.
function TestSharedObjectCannotAddPrivateBrand(sharedObj) {
function Base() {
return sharedObj;
}
assertThrows(() => {
class C extends Base {
#o() {}
}
return new C();
});
}
TestSharedObjectCannotAddPrivateBrand(new Atomics.Condition());
TestSharedObjectCannotAddPrivateBrand(new Atomics.Mutex());
TestSharedObjectCannotAddPrivateBrand(new (new SharedStructType(['p'])));
TestSharedObjectCannotAddPrivateBrand(new SharedArray(1));

View File

@ -0,0 +1,25 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
// Private fields should not be able to extend shared objects. This is unlike
// ordinary objects, which can get private fields even if sealed/frozen.
function TestSharedObjectCannotAddPrivateField(sharedObj) {
function Base() {
return sharedObj;
}
assertThrows(() => {
class C extends Base {
#foo = 42
}
return new C();
});
}
TestSharedObjectCannotAddPrivateField(new Atomics.Condition());
TestSharedObjectCannotAddPrivateField(new Atomics.Mutex());
TestSharedObjectCannotAddPrivateField(new (new SharedStructType(['p'])));
TestSharedObjectCannotAddPrivateField(new SharedArray(1));

View File

@ -0,0 +1,13 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
// Private names as used by V8-specific APIs like Error.captureStackTrace should
// not bypass shared objects' extensibility check.
assertThrows(() => Error.captureStackTrace(new Atomics.Condition()));
assertThrows(() => Error.captureStackTrace(new Atomics.Mutex()));
assertThrows(() => Error.captureStackTrace(new (new SharedStructType(['p']))));
assertThrows(() => Error.captureStackTrace(new SharedArray(1)));

View File

@ -0,0 +1,23 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --expose-externalize-string --shared-string-table
(function __f_0() {
let worker = new Worker('', {type: 'string'});
})();
function __f_11() { return "A"; }
function __f_12() { return "\u1234"; }
function __f_13() {
var __v_3 = "";
var __v_5 =
"AAAA" + __f_11();
var __v_6 =
"\u1234\u1234\u1234\u1234" + __f_12();
gc();
externalizeString(__v_6);
}
for (var __v_4 = 0; __v_4 < 10; __v_4++) {
__f_13();
}

View File

@ -0,0 +1,14 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table
let o = {}
let json_str = '[{"a": 2.1, "b": 1, "c": "hello"}, {"a": null, "b": 2, "c": {"a": 2.1, "b": 1.1, "c": "hello"}}]';
// Internalize a bunch of strings to trigger a shared GC.
for (let i=0; i<100; i++) {
let str = 'X'.repeat(100) + i;
o[str] = str;
}
JSON.parse('[{"a": 2.1, "b": 1, "c": "hello"}, {"a": null, "b": 2, "c": {"a": 2.1, "b": 1.1, "c": "hello"}}]');

View File

@ -0,0 +1,19 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --expose-gc --expose-externalize-string
const str = String.fromCharCode(849206214, 00, 00);
gc();
const Bar = this.SharedStructType(new String('a'));
const bar = Bar();
bar.a = str;
externalizeString(str);
bar[str] = 'foo';
const str2 = String.fromCharCode(849206214, 00, 00);
gc();
const bar2 = Bar();
bar2.a = str2;
externalizeString(str2);
bar2[str2] = 'foo';

View File

@ -0,0 +1,15 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
const t0 = this.SharedStructType(['a']);
const v6 = t0();
function f10(a11) {
a11[1] = 0.1;
}
f10(Array());
f10(v6);
f10(v6);
assertEquals(undefined, v6[1]);

View File

@ -0,0 +1,20 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-struct
const sharedCtors = [
new SharedStructType(['x']), SharedArray, Atomics.Mutex, Atomics.Condition
];
for (let ctor of sharedCtors) {
(function TestSharedConstructors(C) {
function f(a) {
a.stack = "boom";
}
%PrepareFunctionForOptimization(f);
Error.captureStackTrace(f);
f(C);
})(ctor);
}

View File

@ -0,0 +1,13 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --jit-fuzzing --harmony-struct
function f(a) { a[4] = 42; }
for (let i = 0; i < 2; i++) {
f([]);
f(Atomics.Mutex());
f(Atomics.Condition());
}

View File

@ -0,0 +1,29 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-externalize-string --shared-string-table
// Flags: --allow-natives-syntax
function CheckExternal() {
let o = {};
const key = "25964";
externalizeString(key);
o[key] = 'foo';
assertTrue(Reflect.has(o, key));
}
CheckExternal();
function CheckInternalizedExternal() {
let o = {};
const key = createExternalizableString("259" + "63");
const shared_key = %ShareObject(key);
externalizeString(shared_key);
%InternalizeString("25963");
%InternalizeString(shared_key);
o[shared_key] = 'foo';
assertTrue(Reflect.has(o, shared_key));
}
CheckInternalizedExternal();

View File

@ -0,0 +1,16 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-externalize-string --shared-string-table
let o = {};
const key = "12345";
externalizeString(key);
let handler = {
has(target, key) {
return Reflect.has(target, key);
}
}
let p = new Proxy(o, handler);
assertFalse(Reflect.has(p, key));

View File

@ -0,0 +1,27 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-struct --expose-gc
const Probe = function() {
const ProxyConstructor = Proxy;
const getPrototypeOf = Object.getPrototypeOf;
const setPrototypeOf = Object.setPrototypeOf;
function probe(value) {
originalPrototype = getPrototypeOf(value);
newPrototype = new ProxyConstructor(originalPrototype, function(){});
setPrototypeOf(value, newPrototype);
}
return {
probe: probe,
};
}();
gc();
for (let v2 = 0; v2 < 5; v2++) {
// Creates a derived map with non-JSObject prototype.
Probe.probe(Atomics);
Atomics.h = Atomics.Mutex();
}

View File

@ -0,0 +1,30 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
(function TestAsyncLockFailsOnStackOveflow() {
let count = 0;
let error;
let mutex = new Atomics.Mutex;
(function f() {
try { f() } catch (e) {}
if (count === 0) {
count++;
try {
// This call will fail because internally it calls the promise_then
// builtin which will cause a stack overflow.
Atomics.Mutex.lockAsync(mutex, () => {});
} catch (e) {
// Don't call functions in here to avoid stack overflow.
error = e;
}
}
})();
// The lockAsync call failed and the waiter nodes were cleaned up.
assertEquals("Maximum call stack size exceeded", error.message);
assertEquals("RangeError", error.name);
assertEquals(0, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(mutex));
assertEquals(0, %AtomicsSychronizationNumAsyncWaitersInIsolateForTesting());
})();

View File

@ -0,0 +1,17 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
function AWolfInPromiseClothing(executor) {
executor(()=>{}, ()=>{});
}
Object.defineProperty(Promise, Symbol.species, {
get() {
return AWolfInPromiseClothing;
}
});
assertThrows(
()=>Atomics.Mutex.lockAsync(new Atomics.Mutex(), async ()=>{}),
TypeError);

View File

@ -0,0 +1,21 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
(function TestAsyncLockProxyAsCallback() {
const callable = new Proxy(eval, {});
const mutex = new Atomics.Mutex;
const locAsyncPromise = Atomics.Mutex.lockAsync(mutex, callable);
let thenExecuted = false;
locAsyncPromise.then(() => {thenExecuted = true}, () => {});
const asyncLoop = () => {
if (!thenExecuted) {
setTimeout(asyncLoop, 0);
return;
}
};
asyncLoop();
})();

View File

@ -0,0 +1,11 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --invoke-weak-callbacks
let mutex = new Atomics.Mutex();
let cond = new Atomics.Condition();
Atomics.Mutex.lockAsync(mutex, async function () {
await Atomics.Condition.waitAsync(cond, mutex);
});

View File

@ -0,0 +1,39 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-struct --allow-natives-syntax
(function TestNegativeNotify() {
const mutex = new Atomics.Mutex;
const cv = new Atomics.Condition;
let done = false;
let promise = Atomics.Mutex.lockAsync(mutex, async () => {
await Atomics.Condition.waitAsync(cv, mutex);
})
promise.then(() => {
done = true;
})
const cleanupLoop = () => {
assertEquals(0, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv));
if (done) {
return;
}
setTimeout(cleanupLoop, 0);
};
const notifyLoop = () => {
if (%AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv) === 1) {
Atomics.Condition.notify(cv, -14);
assertEquals(
1, %AtomicsSynchronizationPrimitiveNumWaitersForTesting(cv));
assertFalse(done);
Atomics.Condition.notify(cv);
cleanupLoop();
return;
}
setTimeout(notifyLoop, 0);
};
notifyLoop();
})();

View File

@ -0,0 +1,40 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct
// If Promise.prototype.then is set to a non-callable, builtin
// promises become non thenables, hence any promise resolve to
// them calls its reactions instead of queing a then task.
// In the current implementation an internal promise with
// a reaction to release the lock is resolved to the result of
// the asyncLock callback, causing the unlock microtask to
// be executed before the asyncLock callback finishes. This test
// should be updated if the behavior intentionally changes.
(function TestAsyncWaitPromisePrototypeTampering() {
const mutex = new Atomics.Mutex;
const cv = new Atomics.Condition;
let finishedExecution = false;
let startedExecution = false;
let lockAsyncPromise = Atomics.Mutex.lockAsync(mutex, async () => {
startedExecution = true;
await Atomics.Condition.waitAsync(cv, mutex);
finishedExecution = true;
});
let thenExecuted = false;
lockAsyncPromise.then(() => {
thenExecuted = true;
});
Promise.prototype.then = undefined;
// Run Microtasks
setTimeout(() => {
assertTrue(startedExecution);
assertFalse(finishedExecution);
// The lockAsyncPromise is resolved even though the async callback
// didn't finish executing.
assertTrue(thenExecuted);
}, 0);
})();

View File

@ -0,0 +1,86 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
'use strict';
if (this.Worker) {
(function TestSharedArrayPostMessage() {
let workerScript = `onmessage = function({data:arr}) {
// Non-atomic write that will be made visible once main thread
// observes the atomic write below.
arr[0][0] = 42;
arr[1].payload = 84;
Atomics.store(arr, 2, "worker");
};
postMessage("started");`;
let worker = new Worker(workerScript, {type: 'string'});
let started = worker.getMessage();
assertEquals('started', started);
let OuterArray = new SharedArray(3);
let Struct = new SharedStructType(['payload']);
OuterArray[0] = new SharedArray(1);
OuterArray[1] = new Struct();
OuterArray[2] = 'main';
assertEquals(undefined, OuterArray[0][0]);
assertEquals(undefined, OuterArray[1].payload);
assertEquals('main', OuterArray[2]);
worker.postMessage(OuterArray);
// Spin until we observe the worker's write on index 2.
while (Atomics.load(OuterArray, 2) !== 'worker') {
}
// The non-atomic store write must also be visible.
assertEquals(42, OuterArray[0][0]);
assertEquals(84, OuterArray[1].payload);
worker.terminate();
})();
(function TestCompareExchange() {
// Test that we can perform compareExchange simultaneously on 2 threads.
// One thread will update the value only after the other thread has
// performed an update.
const ST = new SharedStructType(['field']);
const s = new ST();
const arr = new SharedArray(1);
// Odd number of values so the main thread perfoms the last compareExchange.
let testValues =
[0, 1.5, undefined, null, true, false, 'foo', s, new SharedArray(1)];
let sharedtestValues = new SharedArray(testValues.length);
Object.assign(sharedtestValues, testValues);
let workerScript = `onmessage = function({data:e}) {
const arr = e[0];
let i = 0;
const testValues = e[1];
while(i < testValues.length - 1) {
let value = testValues[i];
let nextValue = testValues[i+1];
if (value === Atomics.compareExchange(arr, 0, value, nextValue)) {
i += 2;
}
}
}`
let worker = new Worker(workerScript, {type: 'string'});
worker.postMessage([arr, sharedtestValues]);
arr[0] = testValues[0];
let i = 1;
while (i < testValues.length - 1) {
let value = testValues[i];
let nextValue = testValues[i + 1];
if (value === Atomics.compareExchange(arr, 0, value, nextValue)) {
i += 2;
}
}
assertEquals(testValues[testValues.length - 1], arr[0]);
worker.terminate();
})();
}

View File

@ -0,0 +1,131 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct --allow-natives-syntax
'use strict';
(function TestPrimitivesUsingAtomics() {
// All primitives can be stored in fields.
const prims = [42, -0, undefined, null, true, false, 'foo'];
const arr = new SharedArray(1);
for (let prim of prims) {
Atomics.store(arr, 0, prim);
assertEquals(Atomics.load(arr, 0), prim);
}
for (let prim1 of prims) {
for (let prim2 of prims) {
arr[0] = prim1;
assertEquals(Atomics.exchange(arr, 0, prim2), prim1);
assertEquals(arr[0], prim2);
}
}
const primsCopy = [42, -0, undefined, null, true, false, 'foo'];
for (let i = 0; i < prims.length; i++) {
const prim = prims[i];
const primCopy = primsCopy[i];
arr[0] = prim;
// Atomics.compareExchange mimics `===` comparison.
assertEquals(Atomics.compareExchange(arr, 0, '42', 'exchanged'), prim);
assertEquals(arr[0], prim);
assertEquals(Atomics.compareExchange(arr, 0, primCopy, 'exchanged'), prim);
assertEquals(arr[0], 'exchanged');
}
})();
(function TestObjectsUsingAtomics() {
let arr = new SharedArray(1);
// Shared objects cannot point to non-shared objects.
assertThrows(() => {
Atomics.store(arr, 0, []);
});
assertThrows(() => {
Atomics.store(arr, 0, {});
});
// Shared objects can point to other shared objects.
let shared_rhs = new SharedArray(10);
Atomics.store(arr, 0, shared_rhs);
assertEquals(Atomics.load(arr, 0), shared_rhs);
let Struct = new SharedStructType(['field']);
shared_rhs = new Struct();
Atomics.store(arr, 0, shared_rhs);
assertEquals(Atomics.load(arr, 0), shared_rhs);
// Test we can exchange shared objects.
let shared_rhs2 = new SharedArray(10);
assertEquals(Atomics.exchange(arr, 0, shared_rhs2), shared_rhs);
assertEquals(arr[0], shared_rhs2);
assertEquals(Atomics.exchange(arr, 0, shared_rhs), shared_rhs2);
assertEquals(arr[0], shared_rhs);
// Verify we can compare-exchange shared objects.
// Comparing to a different instance of the same type is not succesful.
assertEquals(
Atomics.compareExchange(arr, 0, new Struct(), 'exchanged'), shared_rhs);
assertNotEquals(arr[0], 'exchanged');
assertEquals(
Atomics.compareExchange(arr, 0, shared_rhs, shared_rhs2), shared_rhs);
assertEquals(arr[0], shared_rhs2);
// Comparing to a different instance of the same type is not succesful.
assertEquals(
Atomics.compareExchange(arr, 0, new SharedArray(10), 'exchanged'),
shared_rhs2);
assertNotEquals(arr[0], 'exchanged');
assertEquals(
Atomics.compareExchange(arr, 0, shared_rhs2, shared_rhs), shared_rhs2);
assertEquals(arr[0], shared_rhs);
})();
(function TestCompareExchangeNumbers() {
const arr = new SharedArray(1);
// We can compare a Smi to a HeapNumber.
arr[0] = 0;
assertTrue(%IsSmi(arr[0]));
let HeapNumber = %AllocateHeapNumber();
assertEquals(
Atomics.compareExchange(arr, 0, HeapNumber, 'exchanged'), HeapNumber);
assertEquals(arr[0], 'exchanged');
// We can compare the same value stored in 2 different HeapNumbers;
arr[0] = 1.5;
HeapNumber = 1.5;
assertFalse(%IsSameHeapObject(arr[0], HeapNumber));
assertEquals(Atomics.compareExchange(arr, 0, HeapNumber, 'exchanged'), 1.5);
assertEquals(arr[0], 'exchanged');
})();
(function TestOutOfBounds() {
let arr = new SharedArray(1);
// Shared structs are non-extensible.
assertThrows(() => {
Atomics.store(arr, 2, 42);
});
assertThrows(() => {
Atomics.store(arr, -1, 42);
});
assertThrows(() => {
Atomics.store(arr, 'field', 42);
});
})();
(function TestLengthReadOnly() {
let arr = new SharedArray(2);
assertEquals(2, Atomics.load(arr, 'length'));
// The 'length' property is read-only. Atomics.store and Atomics.exchange are
// treated like strict functions, and so an error is always thrown.
assertThrows(() => {
Atomics.store(arr, 'length', 42);
});
assertThrows(() => {
Atomics.exchange(arr, 'length', 42);
});
})();

View File

@ -0,0 +1,12 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
(function TestArrayConcatSharedArrayOnArrayProto() {
let sarr = new SharedArray();
Object.defineProperty(
Array.prototype, 0, {writable: true, enumerable: true, value: 42});
print(Object.setPrototypeOf(Array.prototype, sarr).concat());
})();

View File

@ -0,0 +1,116 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
'use strict';
(function TestNoPrototype() {
// For now the experimental shared arrays don't have a prototype.
assertNull(Object.getPrototypeOf(new SharedArray(10)));
assertNull(SharedArray.prototype);
assertThrows(() => {
SharedArray.prototype = {};
});
})();
(function TestPrimitives() {
// All primitives can be stored in fields.
let test_values =
[42, -0, Math.random(), undefined, null, true, false, 'foo'];
let arr = new SharedArray(test_values.length);
let i = 0;
for (let prim of test_values) {
arr[i] = prim;
assertEquals(arr[i], prim);
i++;
}
})();
(function TestObjects() {
let arr = new SharedArray(5);
// Shared objects cannot point to non-shared objects.
assertThrows(() => {
arr[0] = [];
});
assertThrows(() => {
arr[1] = {};
});
// Shared objects can point to other shared objects.
let S = new SharedStructType(['field']);
let shared_rhs = new S();
arr[3] = shared_rhs;
assertEquals(arr[3], shared_rhs);
shared_rhs = new SharedArray(10)
arr[4] = shared_rhs;
assertEquals(arr[4], shared_rhs);
})();
(function TestNotExtensible() {
let arr = new SharedArray(1);
// Shared structs are non-extensible.
assertThrows(() => {
arr[1] = 42;
});
assertThrows(() => {
Object.setPrototypeOf(arr, {});
});
assertThrows(() => {
Object.defineProperty(arr, 'nonExistent', {value: 42});
});
})();
(function TestBounds() {
assertThrows(
() => {
new SharedArray(2 ** 32);
},
RangeError,
'SharedArray length out of range');
assertThrows(
() => {
new SharedArray(-1);
},
RangeError,
'SharedArray length out of range');
})();
(function TestOwnPropertyEnumeration() {
let shared_array = new SharedArray(2);
shared_array[0] = 42;
assertEquals(2, shared_array.length);
let propDescs = Object.getOwnPropertyDescriptors(shared_array);
let desc = propDescs[0];
assertEquals(true, desc.writable);
assertEquals(false, desc.configurable);
assertEquals(true, desc.enumerable);
assertEquals(42, desc.value);
let vals = Object.values(shared_array);
assertArrayEquals([42, undefined], vals);
let entries = Object.entries(shared_array);
assertEquals(2, entries.length);
assertArrayEquals(['0', 42], entries[0]);
})();
(function TestForIn() {
let shared_array = new SharedArray(2);
let i = 0;
for (let index in shared_array) {
assertEquals(index, String(i));
i++;
}
})();
(function TestProxyLengthGetter() {
let a = new SharedArray(2);
let proxy = new Proxy(a, {});
assertEquals(2, proxy.length);
})();

View File

@ -0,0 +1,56 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
// --allow-natives-syntax
'use strict';
if (this.Worker) {
(function TestSharedArrayPostMessage() {
let workerScript = `onmessage = function({data:arr}) {
arr[0][0] = 42;
arr[1] = "worker";
arr[2].payload = "updated";
postMessage("done");
};
postMessage("started");`;
let worker = new Worker(workerScript, {type: 'string'});
let started = worker.getMessage();
assertEquals('started', started);
let outer_arr = new SharedArray(3);
outer_arr[0] = new SharedArray(1);
outer_arr[1] = 'main';
let Struct = new SharedStructType(['payload']);
let struct = new Struct();
struct.payload = 'original';
outer_arr[2] = struct;
assertEquals('main', outer_arr[1]);
assertEquals(undefined, outer_arr[0][0]);
worker.postMessage(outer_arr);
assertEquals('done', worker.getMessage());
assertEquals('worker', outer_arr[1]);
assertEquals(42, outer_arr[0][0]);
assertEquals('updated', outer_arr[2].payload);
worker.terminate();
})();
(function TestObjectAssign() {
function f() {
const shared_array = new SharedArray(1);
const array = new Array(1);
array[0] = 1;
Object.assign(shared_array, array);
postMessage(shared_array[0]);
}
const worker = new Worker(f, {'type': 'function'});
assertEquals(1, worker.getMessage());
worker.terminate();
})();
}

View File

@ -0,0 +1,73 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-externalize-string --expose-gc --shared-string-table
const long_key = 'key1234567890abcdefg';
const substr_key = long_key.substring(3,17);
const consstr_key = 'key' + 1234567890 + 'abcdefg';
const integer_index = long_key.substring(3,8);
gc();
gc();
{
let obj = [];
for (let i = 0; i < 100; ++i) {
obj[i] = i;
obj['XXX' + i] = 'XXX' + i;
}
obj['key1234567890abcdefg'] = 'long_key_value';
obj['1234567890abcd'] = 'substr_value';
obj[12345] = 'integer_index';
try {
externalizeString(long_key);
externalizeString(substr_key);
externalizeString(consstr_key);
externalizeString(integer_index);
} catch {}
(function exerciseICs() {
for (let i = 0; i < 10; i++) {
assertEquals('long_key_value', obj[long_key]);
assertEquals('substr_value', obj[substr_key]);
assertEquals('long_key_value', obj[consstr_key]);
assertEquals('integer_index', obj[integer_index]);
}
})();
}
gc();
gc();
// Test storing to dictionary slow-path for shared strings.
{
let obj = [];
for (let i = 0; i < 100; ++i) {
obj[i] = i;
obj['XXX' + i] = 'XXX' + i;
}
try {
externalizeString(long_key);
externalizeString(substr_key);
externalizeString(consstr_key);
externalizeString(integer_index);
} catch {}
(function exerciseICs() {
for (let i = 0; i < 10; i++) {
obj['key1234567890abcdefg'] = 'long_key_value';
obj['1234567890abcd'] = 'substr_value';
obj[12345] = 'integer_index';
assertEquals('long_key_value', obj[long_key]);
assertEquals('substr_value', obj[substr_key]);
assertEquals('long_key_value', obj[consstr_key]);
assertEquals('integer_index', obj[integer_index]);
}
})();
}

View File

@ -0,0 +1,40 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-externalize-string --shared-string-table
// Flags: --allow-natives-syntax
function set(o, ext_key) {
o[ext_key] = "bar";
}
function get(o, ext_key) {
o[ext_key];
}
%PrepareFunctionForOptimization(set);
%OptimizeFunctionOnNextCall(set);
%PrepareFunctionForOptimization(get);
%OptimizeFunctionOnNextCall(get);
(function test() {
let ext_key = createExternalizableString('AAAAAAAAAAAAAAAAAAAAAA');
try {
externalizeString(ext_key);
} catch {
}
set({a:1}, ext_key);
set({b:2}, ext_key);
set({c:3}, ext_key);
set({d:4}, ext_key);
set({e:5}, ext_key);
set({f:6}, ext_key);
get({a:1}, ext_key);
get({b:2}, ext_key);
get({c:3}, ext_key);
get({d:4}, ext_key);
get({e:5}, ext_key);
get({f:6}, ext_key);
})();

View File

@ -0,0 +1,10 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-heap --harmony
'use strict';
let workerScript = `onmessage = function() {}`;
let worker = new Worker(workerScript, {type: 'string'});

View File

@ -0,0 +1,119 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
'use strict';
let S1 = new SharedStructType(['field']);
let S2 = new SharedStructType(['other_field']);
let S3 = new SharedStructType(['field']);
let other_obj = {'field': 42};
function OtherConstructor(val) {
return {'val': val + 1};
}
function BuildPrototypeChain(base_obj) {
let proto = {};
Object.setPrototypeOf(proto, base_obj);
for (let i = 0; i < 10; i++) {
let temp = Object.create(proto);
proto = temp;
}
const handler = {};
for (let i = 0; i < 10; i++) {
let temp = new Proxy(proto, handler);
proto = temp;
}
return proto;
}
(function TestSharedStructHasInstance() {
let s1 = new S1();
// Returns true for the original constructor.
assertTrue(s1 instanceof S1);
// Returns false for a SharedStructType with a different fields.
assertFalse(s1 instanceof S2);
// Returns false for a different SharedStructType with the same fields.
assertFalse(s1 instanceof S3);
// Returns false for a different object.
assertFalse(other_obj instanceof S1);
// Returns false for a different constructor.
assertFalse(s1 instanceof OtherConstructor);
// Returns true if the shared struct is in the prototype chain.
assertTrue(BuildPrototypeChain(s1) instanceof S1);
})();
(function TestSharedArrayHasInstance() {
let sa = new SharedArray(1);
let sb = new SharedArray(2);
// Like regular Arrays, returns true for SharedArray of any length.
assertTrue(sa instanceof SharedArray);
assertTrue(sb instanceof SharedArray);
// Returns false for a different object.
assertFalse(other_obj instanceof SharedArray);
// Returns false for a different constructor.
assertFalse(sa instanceof OtherConstructor);
// Returns true if the SharedArray is in the prototype chain.
assertTrue(BuildPrototypeChain(sa) instanceof SharedArray);
})();
(function TestAtomicsMutexHasInstance() {
let lock = new Atomics.Mutex();
// Returns true for the original constructor.
assertTrue(lock instanceof Atomics.Mutex);
// Returns false for a different object.
assertFalse(other_obj instanceof Atomics.Mutex);
// Returns false for a different constructor.
assertFalse(lock instanceof OtherConstructor);
// Returns true if the mutex is in the prototype chain.
assertTrue(BuildPrototypeChain(lock) instanceof Atomics.Mutex);
})();
(function TestAtomicsConditionHasInstance() {
let conditon = new Atomics.Condition();
// Returns true for the original constructor.
assertTrue(conditon instanceof Atomics.Condition);
// Returns false for a different object.
assertFalse(other_obj instanceof Atomics.Condition);
// Returns false for a different constructor.
assertFalse(conditon instanceof OtherConstructor);
// Returns true if the condition is in the prototype chain.
assertTrue(BuildPrototypeChain(conditon) instanceof Atomics.Condition);
})();
(function TestCallStaticHasInstance() {
let s1 = new S1();
// Returns true when calling with the correct constructor as the receiver.
assertTrue(S1[Symbol.hasInstance].call(S1, s1));
// Returns true when calling with the correct constructor as the receiver from
// another shared object's constructor.
assertTrue(SharedArray[Symbol.hasInstance].call(S1, s1));
// Returns false when calling with other non-shared constructor as the
// receiver.
assertFalse(S1[Symbol.hasInstance].call(OtherConstructor, s1));
// Returns false when calling with a non-function as receiver.
assertFalse(S1[Symbol.hasInstance].call({}, s1));
})();
(function TestRecursivePrototype() {
// Test that we throw when reaching __proto__ recursion limit.
const v = {}, handler = {};
const newPrototype = new Proxy(v, handler);
Object.setPrototypeOf(v, newPrototype);
assertThrows(()=>{v instanceof SharedArray});
})();
(function TestInheritHasInstance() {
const func = () => {};
const sa = new SharedArray(1);
Object.setPrototypeOf(func, SharedArray);
assertFalse(sa instanceof func);
})();

View File

@ -0,0 +1,53 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --shared-string-table --allow-natives-syntax
// Flags: --expose-gc
if (this.Worker) {
(function TestSharedStringPostMessage() {
function workerCode() {
let Box = new SharedStructType(['payload']);
let b1 = new Box();
b1.payload = "started";
postMessage(b1);
onmessage = function({data:box}) {
if (!%IsSharedString(box.payload)) {
throw new Error("str isn't shared");
}
let b2 = new Box();
b2.payload = box.payload;
postMessage(b2);
};
}
// Strings referenced in shared structs are serialized by sharing.
let worker = new Worker(workerCode, { type: 'function' });
let started = worker.getMessage();
assertTrue(%IsSharedString(started.payload));
assertEquals("started", started.payload);
let Box = new SharedStructType(['payload']);
let box_to_send = new Box();
// Create a string in large object space.
let payload = %FlattenString('a'.repeat(1024 * 256));
assertTrue(%InLargeObjectSpace(payload));
// Trigger a gc to move the object to old space.
gc();
assertFalse(%InYoungGeneration(payload));
box_to_send.payload = payload;
assertTrue(%IsSharedString(box_to_send.payload));
worker.postMessage(box_to_send);
let box_received = worker.getMessage();
assertTrue(%IsSharedString(box_received.payload));
assertFalse(box_to_send === box_received);
// Object.is and === won't check pointer equality of Strings.
assertTrue(%IsSameHeapObject(box_to_send.payload, box_received.payload));
worker.terminate();
})();
}

View File

@ -0,0 +1,63 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --shared-string-table --allow-natives-syntax
// Flags: --expose-gc
if (this.Worker) {
(async function TestSharedStringPostMessage() {
function workerCode() {
let Box = new SharedStructType(['payload']);
let b1 = new Box();
b1.payload = "started";
postMessage(b1);
onmessage = function({data:box}) {
if (!%IsSharedString(box.payload)) {
throw new Error("str isn't shared");
}
let b2 = new Box();
b2.payload = box.payload;
postMessage(b2);
};
}
// Strings referenced in shared structs are serialized by sharing.
let worker = new Worker(workerCode, { type: 'function' });
let started = worker.getMessage();
assertTrue(%IsSharedString(started.payload));
assertEquals("started", started.payload);
let Box = new SharedStructType(['payload']);
let box_to_send = new Box();
// Create a string that is not internalized by the parser. Create dummy
// strings before and after the string to make sure we have enough live
// objects on the page to get it promoted to old space (for minor MS).
let trash = [];
for (let i = 0; i < 1024 * 32; i++) {
trash.push('a'.repeat(8));
}
let payload = %FlattenString('a'.repeat(1024 * 60));
for (let i = 0; i < 1024 * 32; i++) {
trash.push('a'.repeat(8));
}
// Trigger two async GCs to move the object to old space.
await gc({ type: 'minor', execution: 'async' });
await gc({ type: 'minor', execution: 'async' });
assertFalse(%InLargeObjectSpace(payload));
assertFalse(%InYoungGeneration(payload));
box_to_send.payload = payload;
assertTrue(%IsSharedString(box_to_send.payload));
worker.postMessage(box_to_send);
let box_received = worker.getMessage();
assertTrue(%IsSharedString(box_received.payload));
assertFalse(box_to_send === box_received);
// Object.is and === won't check pointer equality of Strings.
assertTrue(%IsSameHeapObject(box_to_send.payload, box_received.payload));
worker.terminate();
})();
}

View File

@ -0,0 +1,18 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --allow-natives-syntax --stress-compaction
function foo() { return "foo"; }
%PrepareFunctionForOptimization(foo);
let value = foo();
assertTrue(%IsSharedString(value));
%OptimizeFunctionOnNextCall(foo);
value = foo();
assertTrue(%IsSharedString(value));
%SharedGC();
value = foo();
assertTrue(%IsSharedString(value));
assertEquals("foo", value);

View File

@ -0,0 +1,22 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --allow-natives-syntax --shared-string-table --harmony-struct
var ST = new SharedStructType(['foo']);
var t22 = new ST();
function f() {
t22.foo = 'a'.repeat(9);
globalThis[Symbol.unscopables] = t22.foo;
return globalThis[Symbol.unscopables];
}
%PrepareFunctionForOptimization(f);
for (let i = 0; i < 10; i++) {
f();
}
%OptimizeFunctionOnNextCall(f);
assertEquals('a'.repeat(9), f());

View File

@ -0,0 +1,23 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --allow-natives-syntax --stress-compaction --expose-gc
const val1 = "some value";
assertTrue(%IsSharedString(val1));
const wm = new WeakMap();
const key1 = {};
wm.set(key1, val1);
assertTrue(wm.get(key1) == val1);
assertTrue(%IsSharedString(wm.get(key1)));
gc();
assertTrue(wm.get(key1) == val1);
assertTrue(%IsSharedString(wm.get(key1)));
%SharedGC();
assertTrue(wm.get(key1) == val1);
assertTrue(%IsSharedString(wm.get(key1)));

View File

@ -0,0 +1,27 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --allow-natives-syntax --verify-heap --shared-string-table
const old = {};
old.bar = 100;
gc();
assertFalse(%InYoungGeneration(old));
const foo = 'a'.repeat(9);
assertTrue(%InYoungGeneration(foo));
assertTrue(%IsInPlaceInternalizableString(foo));
// Create old-to-new reference.
old.foo = foo;
// Full GC would usally promote that string into old space,
// with --shared-string-table it is promoted into shared heap
// instead. This should create an old-to-shared reference from
// an old-to-new slot.
gc();
// An additional full GC for heap verification.
gc();

View File

@ -0,0 +1,28 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-gc --allow-natives-syntax --verify-heap --shared-string-table
const old = {};
old.bar = 100;
gc();
assertFalse(%InYoungGeneration(old));
const foo = 'a'.repeat(9);
assertTrue(%InYoungGeneration(foo));
assertTrue(%IsInPlaceInternalizableString(foo));
// Create old-to-new reference.
old.foo = foo;
// The second minor GC would usally promote that string
// into old space, with --shared-string-table it is promoted
// into shared heap instead. This should create an old-to-shared
// reference from an old-to-new slot.
gc({type: "minor"});
gc({type: "minor"});
// An additional full GC for heap verification.
gc();

View File

@ -0,0 +1,47 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --shared-string-table --allow-natives-syntax
if (this.Worker) {
(function TestSharedStringPostMessage() {
let workerScript =
`let Box = new SharedStructType(['payload']);
let b1 = new Box();
b1.payload = "started";
postMessage(b1);
onmessage = function({data:box}) {
if (!%IsSharedString(box.payload)) {
throw new Error("str isn't shared");
}
let b2 = new Box();
b2.payload = box.payload;
postMessage(b2);
};`;
// Strings referenced in shared structs are serialized by sharing.
let worker = new Worker(workerScript, { type: 'string' });
let started = worker.getMessage();
assertTrue(%IsSharedString(started.payload));
assertEquals("started", started.payload);
// The string literal appears in source and is internalized, so should
// already be shared.
let Box = new SharedStructType(['payload']);
let box_to_send = new Box();
box_to_send.payload = 'foo';
assertTrue(%IsSharedString(box_to_send.payload));
worker.postMessage(box_to_send);
let box_received = worker.getMessage();
assertTrue(%IsSharedString(box_received.payload));
assertFalse(box_to_send === box_received);
// Object.is and === won't check pointer equality of Strings.
assertTrue(%IsSameHeapObject(box_to_send.payload, box_received.payload));
worker.terminate();
})();
}

View File

@ -0,0 +1,84 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
"use strict";
if (this.Worker) {
(function TestSharedStructPostMessage() {
let workerScript =
`onmessage = function({data:struct}) {
// Non-atomic write that will be made visible once main thread
// observes the atomic write below.
struct.struct_field.payload = 42;
Atomics.store(struct, "string_field", "worker");
};
postMessage("started");`;
let worker = new Worker(workerScript, { type: 'string' });
let started = worker.getMessage();
assertEquals("started", started);
let OuterStruct = new SharedStructType(['string_field', 'struct_field']);
let InnerStruct = new SharedStructType(['payload']);
let struct = new OuterStruct();
struct.struct_field = new InnerStruct();
struct.string_field = "main";
assertEquals("main", struct.string_field);
assertEquals(undefined, struct.struct_field.payload);
worker.postMessage(struct);
// Spin until we observe the worker's write of string_field.
while (Atomics.load(struct, "string_field") !== "worker") {}
// The non-atomic store write must also be visible.
assertEquals(42, struct.struct_field.payload);
worker.terminate();
})();
(function TestCompareExchange() {
// Test that we can perform compareExchange simultaneously on 2 threads.
// One thread will update the value only after the other thread has
// performed an update.
const ST = new SharedStructType(['field']);
const s1 = new ST();
const s = new ST();
// Odd number of values so the main thread perfoms the last compareExchange.
let testValues =
[0, 1.5, undefined, null, true, false, 'foo', s1, new SharedArray(1)];
let sharedtestValues = new SharedArray(testValues.length);
Object.assign(sharedtestValues, testValues);
let workerScript = `onmessage = function({data:e}) {
const s = e[0];
let i = 0;
const testValues = e[1];
while(i < testValues.length - 1) {
let value = testValues[i];
let nextValue = testValues[i+1];
if (value === Atomics.compareExchange(s, 'field', value, nextValue)) {
i += 2;
}
}
}`
let worker = new Worker(workerScript, {type: 'string'});
worker.postMessage([s, sharedtestValues]);
s.field = testValues[0];
let i = 1;
while (i < testValues.length - 1) {
let value = testValues[i];
let nextValue = testValues[i + 1];
if (value === Atomics.compareExchange(s, 'field', value, nextValue)) {
i += 2;
}
}
assertEquals(testValues[testValues.length - 1], s.field);
worker.terminate();
})();
}

View File

@ -0,0 +1,162 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct --allow-natives-syntax
"use strict";
let S = new SharedStructType(['field', '2']);
(function TestPrimitivesUsingAtomics() {
// All primitives can be stored in fields.
let s = new S();
const prims = [42, -0, undefined, null, true, false, "foo"];
for (let prim of prims) {
Atomics.store(s, 'field', prim);
Atomics.store(s, '2', prim);
assertEquals(Atomics.load(s, 'field'), prim);
assertEquals(Atomics.load(s, '2'), prim);
}
for (let prim1 of prims) {
for (let prim2 of prims) {
s.field = prim1;
s[2] = prim1;
assertEquals(Atomics.exchange(s, 'field', prim2), prim1);
assertEquals(s.field, prim2);
assertEquals(Atomics.exchange(s, '2', prim2), prim1);
assertEquals(s[2], prim2);
}
}
const primsCopy = [42, -0, undefined, null, true, false, 'foo'];
for (let i = 0; i < prims.length; i++) {
const prim = prims[i];
const primCopy = primsCopy[i];
s.field = prim;
// Atomics.compareExchange mimics `===` comparison.
assertEquals(Atomics.compareExchange(s, 'field', '42', 'exchanged'), prim);
assertEquals(s.field, prim);
assertEquals(
Atomics.compareExchange(s, 'field', primCopy, 'exchanged'), prim);
assertEquals(s.field, 'exchanged');
s[2] = prim;
assertEquals(Atomics.compareExchange(s, 2, '42', 'exchanged'), prim);
assertEquals(s[2], prim);
assertEquals(Atomics.compareExchange(s, 2, primCopy, 'exchanged'), prim);
assertEquals(s[2], 'exchanged');
}
})();
(function TestObjectsUsingAtomics() {
let s = new S();
// Shared objects cannot point to non-shared objects.
assertThrows(() => { Atomics.store(s, 'field', []); });
assertThrows(() => { Atomics.store(s, 'field', {}); });
// Shared objects can point to other shared objects.
let shared_rhs = new S();
Atomics.store(s, 'field', shared_rhs);
assertEquals(Atomics.load(s, 'field'), shared_rhs);
shared_rhs = new SharedArray(10);
Atomics.store(s, 'field', shared_rhs);
assertEquals(Atomics.load(s, 'field'), shared_rhs);
let shared_rhs2 = new S();
assertEquals(Atomics.exchange(s, 'field', shared_rhs2), shared_rhs);
assertEquals(s.field, shared_rhs2);
assertEquals(Atomics.exchange(s, 'field', shared_rhs), shared_rhs2);
assertEquals(s.field, shared_rhs);
assertEquals(
Atomics.compareExchange(s, 'field', new SharedArray(10), 'exchanged'),
shared_rhs);
assertNotEquals(s.field, 'exchanged');
assertEquals(
Atomics.compareExchange(s, 'field', shared_rhs, shared_rhs2), shared_rhs);
assertEquals(s.field, shared_rhs2);
assertEquals(
Atomics.compareExchange(s, 'field', new S(), 'exchanged'), shared_rhs2);
assertNotEquals(s.field, 'exchanged');
assertEquals(
Atomics.compareExchange(s, 'field', shared_rhs2, shared_rhs),
shared_rhs2);
assertEquals(s.field, shared_rhs);
})();
(function TestNotExtensibleUsingAtomics() {
let s = new S();
// Shared structs are non-extensible.
assertThrows(() => { Atomics.store(s, 'nonExistent', 42); });
})();
(function TestCompareExchangeNumbers() {
let fieldNames = new SharedArray(999);
fieldNames[0] = '2';
for (let i = 1; i < fieldNames.length; i++) {
fieldNames[i] = `field${i}`;
}
let ST = new SharedStructType(fieldNames)
let s = new ST();
// Test in-object fields.
// We can compare a Smi to a HeapNumber.
s.field1 = 0;
assertTrue(%IsSmi(s.field1));
let HeapNumber = %AllocateHeapNumber();
assertEquals(
Atomics.compareExchange(s, 'field1', HeapNumber, 'success'), HeapNumber);
assertEquals(s.field1, 'success');
// We can compare the same value stored in 2 different HeapNumbers.
s.field1 = 1.5;
HeapNumber = 1.5;
assertFalse(%IsSameHeapObject(s.field, HeapNumber));
assertEquals(
Atomics.compareExchange(s, 'field1', HeapNumber, 'success'), 1.5);
assertEquals(s.field1, 'success');
// Test dictionary element fields.
// We can compare a Smi to a HeapNumber.
s[2] = 0;
assertTrue(%IsSmi(s[2]));
HeapNumber = %AllocateHeapNumber();
assertEquals(
Atomics.compareExchange(s, 2, HeapNumber, 'success'), HeapNumber);
assertEquals(s[2], 'success');
// We can compare the same value stored in 2 different HeapNumbers.
s[2] = 1.5;
HeapNumber = 1.5;
assertFalse(%IsSameHeapObject(s[2], HeapNumber));
assertEquals(Atomics.compareExchange(s, 2, HeapNumber, 'success'), 1.5);
assertEquals(s[2], 'success');
// Test out-of-object fields.
// We can compare a Smi to a HeapNumber.
s.field998 = 0;
assertTrue(%IsSmi(s.field998));
HeapNumber = %AllocateHeapNumber();
assertEquals(
Atomics.compareExchange(s, 'field998', HeapNumber, 'success'),
HeapNumber);
assertEquals(s.field998, 'success');
// We can compare the same value stored in 2 different HeapNumbers.
s.field998 = 1.5;
HeapNumber = 1.5;
assertFalse(%IsSameHeapObject(s.field, HeapNumber));
assertEquals(
Atomics.compareExchange(s, 'field998', HeapNumber, 'success'), 1.5);
assertEquals(s.field998, 'success');
})();

View File

@ -0,0 +1,23 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
'use strict';
let fieldNames = [];
for (let i = 0; i < 999; i++) {
fieldNames.push('field' + i);
}
let s = new (new SharedStructType(fieldNames));
// Write to an out-of-object field.
(function storeOutOfObject() {
for (let i = 0; i < 100; i++) s.field998 = i;
})();
// Write to an in-object field.
(function storeInObject() {
for (let i = 0; i < 100; i++) s.field0 = i;
})();

View File

@ -0,0 +1,172 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
"use strict";
let S = new SharedStructType(['field']);
(function TestNoPrototype() {
// For now the experimental shared structs don't have a prototype, unlike the
// proposal explainer which says accessing the prototype throws.
assertNull(S.prototype);
assertNull(Object.getPrototypeOf(new S()));
assertThrows(() => {
S.prototype = {};
});
})();
(function TestPrimitives() {
// All primitives can be stored in fields.
let s = new S();
for (let prim of [42, -0, Math.random(),
undefined, null, true, false,
"foo"]) {
s.field = prim;
assertEquals(s.field, prim);
}
})();
(function TestObjects() {
let s = new S();
// Shared objects cannot point to non-shared objects.
assertThrows(() => { s.field = []; });
assertThrows(() => { s.field = {}; });
// Shared objects can point to other shared objects.
let shared_rhs = new S();
s.field = shared_rhs;
assertEquals(s.field, shared_rhs);
shared_rhs = new SharedArray(10);
s.field = shared_rhs;
assertEquals(s.field, shared_rhs);
})();
(function TestNotExtensible() {
let s = new S();
// Shared structs are non-extensible.
assertThrows(() => { s.nonExistent = 42; });
assertThrows(() => { Object.setPrototypeOf(s, {}); });
assertThrows(() => { Object.defineProperty(s, 'nonExistent', { value: 42 }); });
})();
(function TestTooManyFields() {
let fieldNames = [];
for (let i = 0; i < 1000; i++) {
fieldNames.push('field' + i);
}
assertThrows(() => {
new SharedStructType(fieldNames);
});
})();
(function TestOwnPropertyEnumeration() {
let s = new S();
s.field = 42;
assertArrayEquals(['field'], Reflect.ownKeys(s));
let propDescs = Object.getOwnPropertyDescriptors(s);
let desc = propDescs['field'];
assertEquals(true, desc.writable);
assertEquals(false, desc.configurable);
assertEquals(true, desc.enumerable);
assertEquals(42, desc.value);
let vals = Object.values(s);
assertArrayEquals([42], vals);
let entries = Object.entries(s);
assertEquals(1, entries.length);
assertArrayEquals(['field', 42], entries[0]);
})();
(function TestForIn() {
let fieldNames = [];
for (let i = 0; i < 512; i++) {
fieldNames.push('field' + i);
}
let S2 = new SharedStructType(fieldNames);
let s = new S2();
let propNames = [];
for (let prop in s) propNames.push(prop);
assertArrayEquals(propNames, fieldNames);
})();
(function TestDuplicateFieldNames() {
assertThrows(() => new SharedStructType(['same', 'same']));
})();
(function TestNoFields() {
const EmptyStruct = new SharedStructType([]);
let s = new EmptyStruct();
})();
(function TestSymbolsDisallowed() {
// This may be relaxed in the future.
assertThrows(() => new SharedStructType([Symbol()]));
})();
(function TestUsedAsPrototype() {
const OnPrototypeStruct = new SharedStructType(['prop']);
let ps = new OnPrototypeStruct();
ps.prop = "on proto";
function assertProtoIsStruct(obj, proto) {
// __proto__ is on Object.prototype, and obj here no longer has
// Object.prototype as its [[Prototype]].
assertSame(undefined, obj.__proto__);
assertSame(proto, Object.getPrototypeOf(obj));
assertEquals("on proto", obj.prop);
}
{
let pojo = { __proto__: ps };
assertProtoIsStruct(pojo, ps);
}
{
let pojo = {};
Object.setPrototypeOf(pojo, ps);
assertProtoIsStruct(pojo, ps);
}
{
let pojo = {};
pojo.__proto__ = ps;
assertProtoIsStruct(pojo, ps);
}
{
const old = globalThis.__proto__;
globalThis.__proto__ = ps;
globalThis.__proto__ = old;
}
{
Object.create(ps);
}
{
function Ctor() {}
Ctor.prototype = ps;
}
})();
(function TestElements() {
const largeIndex = 2 ** 32;
const anotherLargeIndex = 2 ** 31;
const StructWithElements =
new SharedStructType([1, 42, largeIndex, anotherLargeIndex]);
let s = new StructWithElements();
s[1] = 0.1;
s[42] = 42;
s[largeIndex] = 'whatever';
s[anotherLargeIndex] = 'whatever2';
assertEquals(0.1, s[1]);
assertEquals(42, s[42]);
assertEquals('whatever', s[largeIndex]);
assertEquals('whatever2', s[anotherLargeIndex]);
assertThrows(() => s[80] = 0.2);
})();

View File

@ -0,0 +1,100 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
(function TestRegistryHitWithFieldsOnly() {
let T1 = new SharedStructType(['x', 'y'], 'Fields');
let T2 = new SharedStructType(['x', 'y'], 'Fields');
assertNotSame(T1, T2);
%HaveSameMap(new T1, new T2);
})();
(function TestRegistryHitWithFieldsInternalized() {
let T1 = new SharedStructType(
[
'qwporugaoasdjioasdjiaods',
'bhuiweuryiq190hvaodeh'
],
'FieldsInternalized');
let T2 = new SharedStructType(
[
%ConstructConsString('qwporugao', 'asdjioasdjiaods'),
%ConstructConsString('bhuiweuryiq', '190hvaodeh')
],
'FieldsInternalized');
assertNotSame(T1, T2);
%HaveSameMap(new T1, new T2);
})();
(function TestRegistryHitWithElementsOnly() {
let T1 = new SharedStructType([0, 4], 'Elements');
let T2 = new SharedStructType([0, 4], 'Elements');
assertNotSame(T1, T2);
%HaveSameMap(new T1, new T2);
})();
(function TestRegistryHitWithElementsOnlyOrderDoesntMatter() {
let T1 = new SharedStructType([0, 4], 'ElementsOrderDoesntMatter');
let T2 = new SharedStructType([4, 0], 'ElementsOrderDoesntMatter');
assertNotSame(T1, T2);
%HaveSameMap(new T1, new T2);
})();
(function TestRegistryHitWithFieldsAndElements() {
let T1 = new SharedStructType([0, 'x', 4, 'y'], 'FieldsAndElements');
let T2 = new SharedStructType([0, 'x', 4, 'y'], 'FieldsAndElements');
assertNotSame(T1, T2);
%HaveSameMap(new T1, new T2);
})();
(function TestRegistryHitWithFieldsAndElements2() {
// Order doesn't matter even when interleaved with field names.
let T1 = new SharedStructType([0, 'x', 4, 'y'], 'FieldsAndElements2');
let T2 = new SharedStructType([0, 4, 'x', 'y'], 'FieldsAndElements2');
assertNotSame(T1, T2);
%HaveSameMap(new T1, new T2);
})();
(function TestRegistryMismatchLength() {
let T1 = new SharedStructType(['x'], 'MismatchLength');
assertThrows(() => {
new SharedStructType(['x', 'y'], 'MismatchLength');
}, TypeError);
})();
(function TestRegistryMismatchFieldNames() {
let T1 = new SharedStructType(['x', 'y'], 'MismatchFieldNames');
assertThrows(() => {
new SharedStructType(['z', 'w'], 'MismatchFieldNames');
}, TypeError);
})();
(function TestRegistryMismatchOrder() {
let T1 = new SharedStructType(['y', 'x'], 'MismatchOrder');
assertThrows(() => {
new SharedStructType(['x', 'y'], 'MismatchOrder');
}, TypeError);
})();
(function TestRegistryMismatchElementsLength() {
let T1 = new SharedStructType([0, 1], 'MismatchElementsLength');
assertThrows(() => {
new SharedStructType([0], 'MismatchElementsLength');
}, TypeError);
})();
(function TestRegistryMismatchElements() {
let T1 = new SharedStructType([0, 1], 'MismatchElements');
assertThrows(() => {
new SharedStructType([2, 3], 'MismatchElements');
}, TypeError);
})();
(function TestRegistryMismatchElements2() {
let T1 = new SharedStructType(['x'], 'MismatchElements2');
assertThrows(() => {
new SharedStructType([0, 1], 'MismatchElements2');
}, TypeError);
})();

View File

@ -0,0 +1,40 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct --allow-natives-syntax
// Flags: --verify-heap
"use strict";
if (this.Worker) {
(function TestSharedStructOptimizedCode() {
let workerScript =
`onmessage = function({data:struct}) {
%PrepareFunctionForOptimization(writePayload);
for (let i = 0; i < 5; i++) writePayload(struct);
%OptimizeFunctionOnNextCall(writePayload);
writePayload(struct);
postMessage("done");
};
function writePayload(struct) {
for (let i = 0; i < 100; i++) {
struct.payload = struct.payload + 1;
}
}
postMessage("started");`;
let worker = new Worker(workerScript, { type: 'string' });
assertEquals("started", worker.getMessage());
let MyStruct = new SharedStructType(['payload']);
let struct = new MyStruct();
struct.payload = 0;
worker.postMessage(struct);
assertEquals("done", worker.getMessage());
worker.terminate();
})();
}

View File

@ -0,0 +1,53 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct --allow-natives-syntax
"use strict";
if (this.Worker) {
(function TestSharedStructPostMessage() {
let workerScript =
`onmessage = function({data:struct}) {
struct.struct_field.payload = 42;
struct.string_field = "worker";
postMessage("done");
};
postMessage("started");`;
let worker = new Worker(workerScript, { type: 'string' });
let started = worker.getMessage();
assertEquals("started", started);
let OuterStruct = new SharedStructType(['string_field', 'struct_field']);
let InnerStruct = new SharedStructType(['payload']);
let struct = new OuterStruct();
struct.struct_field = new InnerStruct();
struct.string_field = "main";
assertEquals("main", struct.string_field);
assertEquals(undefined, struct.struct_field.payload);
worker.postMessage(struct);
assertEquals("done", worker.getMessage());
assertEquals("worker", struct.string_field);
assertEquals(42, struct.struct_field.payload);
worker.terminate();
})();
(function TestObjectAssign() {
function f() {
const Struct = new SharedStructType(['field'])
const shared_struct = new Struct();
const obj = {'field': 1};
Object.assign(shared_struct, obj);
postMessage(shared_struct.field);
}
const worker = new Worker(f, {'type': 'function'});
assertEquals(1, worker.getMessage());
worker.terminate();
})();
}

View File

@ -0,0 +1,29 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-struct --allow-natives-syntax
const Box = new SharedStructType(['payload']);
let a, b;
function f() {
a = SharedArray(4000);
b = new Box();
// Assignment into shared objects have a barrier that ensure the RHS is in
// shared space.
//
// RHS needs to be large enough to be in a HeapNumber. TF then allocates it
// out of the non-shared old space during optimization. If TF incorrectly
// compiles away the barrier, TF optimized code will create shared->local
// edges.
a[0] = 2000000000;
b.payload = 2000000000;
}
%PrepareFunctionForOptimization(f);
for (let i = 0; i < 10; i++) f();
// Verify that TF optimized code does not incorrectly compile away the shared
// value barrier.
%OptimizeFunctionOnNextCall(f);
for (let i = 0; i < 10; i++) f();
// SharedGC will verify there are no shared->local edges.
%SharedGC();

View File

@ -0,0 +1,17 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --expose-gc
let arr = new Array(65535);
gc();
arr[arr.length-1] = 'two';
arr[1] = 'two';
arr[2] = 'two';
arr.length = 2;
gc();
gc();
arr.length = 1;
gc();
gc();

View File

@ -0,0 +1,13 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The value deserializer should be lenient of malformed or malicious data and
// should throw instead of crash on non-existent shared objects in serialized
// data.
(function SharedObject() {
// Shared object is 'p', ASCII 112.
const data = new Uint8Array([255, 15, 112, 0]);
assertThrows(() => { d8.serializer.deserialize(data.buffer); });
})();

View File

@ -0,0 +1,68 @@
// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --verify-heap --shared-heap
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function() {
let num_pages = 64 * 1024 * 1024 / kPageSize;
let builder = new WasmModuleBuilder();
builder.addMemory64(num_pages, num_pages);
builder.exportMemoryAs('memory');
let module = builder.instantiate();
let memory = module.exports.memory;
new Int8Array(memory.buffer);
})();
function InstantiatingWorkerCode() {
function workerAssert(condition, message) {
if (!condition) postMessage(`Check failed: ${message}`);
}
onmessage = function({data:[mem, module]}) {
workerAssert(mem instanceof WebAssembly.Memory, 'Wasm memory');
workerAssert(mem.buffer instanceof SharedArrayBuffer, 'SAB');
try {
new WebAssembly.Instance(module, {imp: {mem: mem}});
postMessage('Instantiation succeeded');
} catch (e) {
postMessage(`Exception: ${e}`);
}
};
}
(function TestImportMemory64AsMemory32InWorker() {
print(arguments.callee.name);
const builder1 = new WasmModuleBuilder();
builder1.addMemory64(1, 1, /* shared */ true);
builder1.exportMemoryAs('mem64');
const instance1 = builder1.instantiate();
const {mem64} = instance1.exports;
let builder2 = new WasmModuleBuilder();
builder2.addImportedMemory('imp', 'mem');
let module2 = builder2.toModule();
let worker = new Worker(InstantiatingWorkerCode, {type: 'function'});
worker.postMessage([mem64, module2]);
})();
(function TestImportMemory32AsMemory64InWorker() {
print(arguments.callee.name);
const builder1 = new WasmModuleBuilder();
builder1.addMemory(1, 1, /* shared */ true);
builder1.exportMemoryAs('mem32');
const instance1 = builder1.instantiate();
const {mem32} = instance1.exports;
let builder2 = new WasmModuleBuilder();
builder2.addImportedMemory(
'imp', 'mem', 1, 1, /* shared */ false, /* memory64 */ true);
let module2 = builder2.toModule();
let worker = new Worker(InstantiatingWorkerCode, {type: 'function'});
worker.postMessage([mem32, module2]);
})();