blob: 80df303192e5d18cd565632e254787ce3b1b0884 [file] [log] [blame]
// Copyright 2019 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.
@abstract
// We normally don't generate a BodyDescriptor for an abstact class, but here we
// do since all context classes share the same BodyDescriptor.
@generateBodyDescriptor
extern class Context extends HeapObject {
macro GetScopeInfo(): ScopeInfo {
return *ContextSlot(this, ContextSlot::SCOPE_INFO_INDEX);
}
const length: Smi;
elements[length]: Object;
}
@cppObjectLayoutDefinition
extern class ScriptContextTable extends HeapObject {
const capacity: Smi;
length: Smi;
names_to_context_index: NameToIndexHashTable;
objects[capacity]: Context;
}
extern class AwaitContext extends Context generates 'TNode<Context>';
extern class BlockContext extends Context generates 'TNode<Context>';
extern class CatchContext extends Context generates 'TNode<Context>';
extern class DebugEvaluateContext extends Context
generates 'TNode<Context>';
extern class EvalContext extends Context generates 'TNode<Context>';
extern class ModuleContext extends Context generates 'TNode<Context>';
extern class ScriptContext extends Context generates 'TNode<Context>';
extern class WithContext extends Context generates 'TNode<Context>';
extern class FunctionContext extends Context generates 'TNode<Context>';
extern macro TryFloat64ToInt32(float64): int32 labels Failed;
extern macro LoadHeapInt32Value(HeapNumber): int32;
extern macro StoreHeapInt32Value(HeapNumber, int32): void;
extern macro AllocateHeapInt32WithValue(int32): HeapNumber;
const kInitialContextSlotValue: Smi = 0;
@export
macro AllocateSyntheticFunctionContext(
nativeContext: NativeContext, slots: constexpr int31): FunctionContext {
return AllocateSyntheticFunctionContext(
nativeContext, Convert<intptr>(slots));
}
macro AllocateSyntheticFunctionContext(
nativeContext: NativeContext, slots: intptr): FunctionContext {
static_assert(slots >= ContextSlot::MIN_CONTEXT_SLOTS);
const map =
*ContextSlot(nativeContext, ContextSlot::FUNCTION_CONTEXT_MAP_INDEX);
const result = new FunctionContext{
map,
length: Convert<Smi>(slots),
elements: ...ConstantIterator<Smi>(kInitialContextSlotValue)
};
InitContextSlot(result, ContextSlot::SCOPE_INFO_INDEX, kEmptyScopeInfo);
InitContextSlot(result, ContextSlot::PREVIOUS_INDEX, Undefined);
return result;
}
extern class NativeContext extends Context;
type Slot<Container : type extends Context, T : type extends Object> extends
intptr;
// We cannot use ContextSlot() for initialization since that one asserts the
// slot has the right type already.
macro InitContextSlot<
ArgumentContext: type, AnnotatedContext: type, T: type, U: type>(
context: ArgumentContext, index: Slot<AnnotatedContext, T>,
value: U): void {
// Make sure the arguments have the right type.
const context: AnnotatedContext = context;
const value: T = value;
dcheck(TaggedEqual(context.elements[index], kInitialContextSlotValue));
context.elements[index] = value;
}
macro ContextSlot<ArgumentContext: type, AnnotatedContext: type, T: type>(
context: ArgumentContext, index: Slot<AnnotatedContext, T>):&T {
const context: AnnotatedContext = context;
return torque_internal::unsafe::ReferenceCast<T>(&context.elements[index]);
}
macro NativeContextSlot<T: type>(
context: NativeContext, index: Slot<NativeContext, T>):&T {
return ContextSlot(context, index);
}
macro NativeContextSlot<T: type>(
context: Context, index: Slot<NativeContext, T>):&T {
return ContextSlot(LoadNativeContext(context), index);
}
macro NativeContextSlot<C: type, T: type>(
implicit context: C)(index: Slot<NativeContext, T>):&T {
return NativeContextSlot(context, index);
}
extern enum ContextSlot extends intptr constexpr 'Context::Field' {
SCOPE_INFO_INDEX: Slot<Context, ScopeInfo>,
// Zero is used for the NativeContext, Undefined is used for synthetic
// function contexts.
PREVIOUS_INDEX: Slot<Context, Context|Zero|Undefined>,
AGGREGATE_ERROR_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
ARRAY_BUFFER_FUN_INDEX: Slot<NativeContext, Constructor>,
ARRAY_BUFFER_NOINIT_FUN_INDEX: Slot<NativeContext, JSFunction>,
ARRAY_BUFFER_MAP_INDEX: Slot<NativeContext, Map>,
ARRAY_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
ARRAY_JOIN_STACK_INDEX: Slot<NativeContext, Undefined|FixedArray>,
OBJECT_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
ITERATOR_RESULT_MAP_INDEX: Slot<NativeContext, Map>,
ITERATOR_MAP_HELPER_MAP_INDEX: Slot<NativeContext, Map>,
ITERATOR_FILTER_HELPER_MAP_INDEX: Slot<NativeContext, Map>,
ITERATOR_TAKE_HELPER_MAP_INDEX: Slot<NativeContext, Map>,
ITERATOR_DROP_HELPER_MAP_INDEX: Slot<NativeContext, Map>,
ITERATOR_FLAT_MAP_HELPER_MAP_INDEX: Slot<NativeContext, Map>,
ITERATOR_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
VALID_ITERATOR_WRAPPER_MAP_INDEX: Slot<NativeContext, Map>,
JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX: Slot<NativeContext, Map>,
JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX: Slot<NativeContext, Map>,
JS_MAP_MAP_INDEX: Slot<NativeContext, Map>,
JS_SET_MAP_INDEX: Slot<NativeContext, Map>,
MATH_RANDOM_CACHE_INDEX: Slot<NativeContext, FixedDoubleArray>,
MATH_RANDOM_INDEX_INDEX: Slot<NativeContext, Smi>,
NUMBER_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
PROXY_REVOCABLE_RESULT_MAP_INDEX: Slot<NativeContext, Map>,
REFLECT_APPLY_INDEX: Slot<NativeContext, Callable>,
REGEXP_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
REGEXP_LAST_MATCH_INFO_INDEX: Slot<NativeContext, RegExpMatchInfo>,
INITIAL_STRING_ITERATOR_MAP_INDEX: Slot<NativeContext, Map>,
INITIAL_ARRAY_ITERATOR_MAP_INDEX: Slot<NativeContext, Map>,
INITIAL_ITERATOR_PROTOTYPE_INDEX: Slot<NativeContext, JSObject>,
SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP: Slot<NativeContext, Map>,
STRICT_ARGUMENTS_MAP_INDEX: Slot<NativeContext, Map>,
SLOPPY_ARGUMENTS_MAP_INDEX: Slot<NativeContext, Map>,
FAST_ALIASED_ARGUMENTS_MAP_INDEX: Slot<NativeContext, Map>,
FUNCTION_CONTEXT_MAP_INDEX: Slot<NativeContext, Map>,
FUNCTION_PROTOTYPE_APPLY_INDEX: Slot<NativeContext, JSFunction>,
STRING_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
UINT8_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
INT8_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
UINT16_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
INT16_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
UINT32_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
INT32_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
FLOAT16_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
FLOAT32_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
FLOAT64_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
UINT8_CLAMPED_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
BIGUINT64_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
BIGINT64_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
RAB_GSAB_UINT8_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_INT8_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_UINT16_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_INT16_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_UINT32_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_INT32_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_FLOAT16_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_FLOAT32_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_FLOAT64_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_UINT8_CLAMPED_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_BIGUINT64_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_BIGINT64_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
ACCESSOR_PROPERTY_DESCRIPTOR_MAP_INDEX: Slot<NativeContext, Map>,
DATA_PROPERTY_DESCRIPTOR_MAP_INDEX: Slot<NativeContext, Map>,
PROMISE_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
PROMISE_THEN_INDEX: Slot<NativeContext, JSFunction>,
PROMISE_PROTOTYPE_INDEX: Slot<NativeContext, JSObject>,
STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX: Slot<NativeContext, Map>,
PROMISE_HOOK_INIT_FUNCTION_INDEX: Slot<NativeContext, Undefined|Callable>,
PROMISE_HOOK_BEFORE_FUNCTION_INDEX: Slot<NativeContext, Undefined|Callable>,
PROMISE_HOOK_AFTER_FUNCTION_INDEX: Slot<NativeContext, Undefined|Callable>,
PROMISE_HOOK_RESOLVE_FUNCTION_INDEX: Slot<NativeContext, Undefined|Callable>,
// @if(V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA)
CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX: Slot<NativeContext, HeapObject>,
BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX: Slot<NativeContext, Map>,
BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX: Slot<NativeContext, Map>,
WRAPPED_FUNCTION_MAP_INDEX: Slot<NativeContext, Map>,
@sameEnumValueAs(MIN_CONTEXT_SLOTS)
CONTEXT_SIDE_TABLE_PROPERTY_INDEX: Slot<Context, HeapObject>,
MIN_CONTEXT_SLOTS,
MIN_CONTEXT_EXTENDED_SLOTS,
...
}
@export
macro LoadContextElement(c: Context, i: intptr): Object {
return c.elements[i];
}
@export
macro LoadContextElement(c: Context, i: Smi): Object {
return c.elements[i];
}
@export
macro LoadContextElement(c: Context, i: constexpr int32): Object {
return c.elements[i];
}
@export
macro LoadScriptContextElement(c: Context, i: intptr): Object {
return LoadScriptContextElementImpl(c, i);
}
@export
macro LoadScriptContextElement(c: Context, i: Smi): Object {
return LoadScriptContextElementImpl(c, SmiUntag(i));
}
@export
macro LoadScriptContextElement(c: Context, i: constexpr int32): Object {
return LoadScriptContextElementImpl(c, i);
}
@export
macro StoreContextElement(c: Context, i: intptr, o: Object): void {
c.elements[i] = o;
}
@export
macro StoreContextElement(c: Context, i: Smi, o: Object): void {
c.elements[i] = o;
}
@export
macro StoreContextElement(c: Context, i: constexpr int32, o: Object): void {
c.elements[i] = o;
}
@export
macro StoreContextElementAndUpdateSideData(
c: Context, i: intptr, o: Object): void {
StoreScriptContextAndUpdateSlotProperty(c, i, o);
}
@export
macro StoreContextElementAndUpdateSideData(
c: Context, i: constexpr int32, o: Object): void {
StoreScriptContextAndUpdateSlotProperty(c, i, o);
}
builtin AllocateIfMutableHeapNumberScriptContextSlot(
n: Object, c: Object, i: Smi): Object {
const context = UnsafeCast<Context>(c);
const index = SmiUntag(i);
return TryLoadMutableHeapNumber(context, index, n);
}
builtin StoreCurrentScriptContextSlotBaseline(o: Object, i: Smi): JSAny {
const context = internal::LoadContextFromBaseline();
const index = SmiUntag(i);
StoreScriptContextAndUpdateSlotProperty(context, index, o);
return Undefined;
}
builtin StoreScriptContextSlotBaseline(
c: Object, o: Object, i: Smi, d: TaggedIndex): JSAny {
let context = UnsafeCast<Context>(c);
let depth = TaggedIndexToIntPtr(d);
while (depth > 0) {
--depth;
context =
UnsafeCast<Context>(context.elements[ContextSlot::PREVIOUS_INDEX]);
}
const index = SmiUntag(i);
StoreScriptContextAndUpdateSlotProperty(context, index, o);
return Undefined;
}
namespace runtime {
extern runtime InvalidateDependentCodeForScriptContextSlot(Context, Object):
JSAny;
} // namespace runtime
macro StoreScriptContextAndUpdateSlotProperty(
c: Context, index: intptr, newValue: Object): void {
const scriptContext = Cast<ScriptContext>(c) otherwise unreachable;
const sideData: Object = *ContextSlot(
scriptContext, ContextSlot::CONTEXT_SIDE_TABLE_PROPERTY_INDEX);
if (sideData == kEmptyFixedArray) {
// No side data (maybe the context was created while the side data
// collection was disabled).
c.elements[index] = newValue;
return;
}
const sideDataIndex = index - ContextSlot::MIN_CONTEXT_EXTENDED_SLOTS;
const sideDataFixedArray: FixedArray =
Cast<FixedArray>(sideData) otherwise unreachable;
const oldValue = c.elements[index];
if (oldValue == TheHole) {
// Setting the initial value.
dcheck(sideDataFixedArray.objects[sideDataIndex] == Undefined);
c.elements[index] = newValue;
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyConst);
return;
}
// If we are assigning the same value, the property won't change.
if (TaggedEqual(oldValue, newValue)) {
return;
}
// If both values are HeapNumbers with the same double value, the property
// won't change either.
if (Is<HeapNumber>(oldValue) && Is<HeapNumber>(newValue)) {
const oldNumber = Cast<HeapNumber>(oldValue) otherwise unreachable;
const newNumber = Cast<HeapNumber>(newValue) otherwise unreachable;
if (oldNumber.value == newNumber.value && oldNumber.value != 0) {
return;
}
}
// From now on, we know the value is no longer a constant.
const data = sideDataFixedArray.objects[sideDataIndex];
let maybeCell: Undefined|ContextSidePropertyCell;
// TODO(victorgomes): Use Smis to avoid untagging.
let property: intptr;
// From now on, we know the value is no longer a constant. If there's a
// DependentCode, invalidate it.
typeswitch (data) {
case (property_raw: Smi): {
maybeCell = Undefined;
property = SmiUntag(property_raw);
}
case (cell: ContextSidePropertyCell): {
maybeCell = cell;
property = SmiUntag(cell.property_details_raw);
}
case (Object): {
// If this is reached, there's a code path which initializes or assigns a
// top-level `let` variable but doesn't update the side data.
unreachable;
}
}
if (property == kContextSidePropertyConst) {
if (maybeCell != Undefined) deferred {
runtime::InvalidateDependentCodeForScriptContextSlot(c, maybeCell);
}
if (IsScriptContextMutableHeapNumberFlag()) {
// It can transition to Smi, MutableInt32, MutableHeapNumber or Other.
if (Is<Smi>(newValue)) {
c.elements[index] = newValue;
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertySmi);
} else if (Is<HeapNumber>(newValue)) {
const newNumber = Cast<HeapNumber>(newValue) otherwise unreachable;
try {
if (!IsScriptContextMutableHeapInt32Flag()) goto NotInt32;
const int32Value =
TryFloat64ToInt32(newNumber.value) otherwise NotInt32;
c.elements[index] = AllocateHeapInt32WithValue(int32Value);
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyInt32);
} label NotInt32 {
c.elements[index] = AllocateHeapNumberWithValue(newNumber.value);
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyHeapNumber);
}
} else {
c.elements[index] = newValue;
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyOther);
}
} else {
// MutableHeapNumber is not supported, just transition the property to
// kOther.
c.elements[index] = newValue;
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyOther);
}
} else if (property == kContextSidePropertySmi) {
if (Is<Smi>(newValue)) {
c.elements[index] = newValue;
} else {
if (maybeCell != Undefined) deferred {
runtime::InvalidateDependentCodeForScriptContextSlot(c, maybeCell);
}
// It can transition to MutableInt32, MutableHeapNumber or Other.
if (Is<HeapNumber>(newValue)) {
const newNumber = Cast<HeapNumber>(newValue) otherwise unreachable;
try {
if (!IsScriptContextMutableHeapInt32Flag()) goto NotInt32;
const int32Value =
TryFloat64ToInt32(newNumber.value) otherwise NotInt32;
c.elements[index] = AllocateHeapInt32WithValue(int32Value);
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyInt32);
} label NotInt32 {
c.elements[index] = AllocateHeapNumberWithValue(newNumber.value);
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyHeapNumber);
}
} else {
c.elements[index] = newValue;
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyOther);
}
}
} else if (property == kContextSidePropertyInt32) {
const oldNumber = Cast<HeapNumber>(oldValue) otherwise unreachable;
if (Is<Smi>(newValue)) {
const newNumber = Cast<Smi>(newValue) otherwise unreachable;
StoreHeapInt32Value(oldNumber, SmiToInt32(newNumber));
} else if (Is<HeapNumber>(newValue)) {
const newNumber = Cast<HeapNumber>(newValue) otherwise unreachable;
try {
if (!IsScriptContextMutableHeapInt32Flag()) goto NotInt32;
const int32Value =
TryFloat64ToInt32(newNumber.value) otherwise NotInt32;
StoreHeapInt32Value(oldNumber, int32Value);
} label NotInt32 {
if (maybeCell != Undefined) deferred {
runtime::InvalidateDependentCodeForScriptContextSlot(c, maybeCell);
}
oldNumber.value = newNumber.value;
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyHeapNumber);
}
} else {
if (maybeCell != Undefined) deferred {
runtime::InvalidateDependentCodeForScriptContextSlot(c, maybeCell);
}
// It can only transition to Other.
c.elements[index] = newValue;
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyOther);
}
} else if (property == kContextSidePropertyHeapNumber) {
// TODO(victorgomes): Add a helper function to avoid duplication.
const oldNumber = Cast<HeapNumber>(oldValue) otherwise unreachable;
if (Is<Smi>(newValue)) {
const newNumber = Cast<Smi>(newValue) otherwise unreachable;
oldNumber.value = SmiToFloat64(newNumber);
} else if (Is<HeapNumber>(newValue)) {
const newNumber = Cast<HeapNumber>(newValue) otherwise unreachable;
oldNumber.value = newNumber.value;
} else {
if (maybeCell != Undefined) deferred {
runtime::InvalidateDependentCodeForScriptContextSlot(c, maybeCell);
}
c.elements[index] = newValue;
// It can only transition to Other.
sideDataFixedArray.objects[sideDataIndex] =
SmiTag(kContextSidePropertyOther);
}
} else {
dcheck(property == kContextSidePropertyOther);
// We should not have a code depending on Other.
dcheck(IsUndefined(maybeCell));
// No need to update side data, this is a sink state...
c.elements[index] = newValue;
}
}
macro LoadScriptContextElementImpl(c: Context, i: intptr): Object {
dcheck(Is<ScriptContext>(c));
const val = c.elements[i];
return TryLoadMutableHeapNumber(c, i, val);
}
macro TryLoadMutableHeapNumber(
c: Context, index: intptr, val: Object): Object {
if (!Is<HeapNumber>(val)) return val;
const scriptContext = Cast<ScriptContext>(c) otherwise unreachable;
const sideData: Object = *ContextSlot(
scriptContext, ContextSlot::CONTEXT_SIDE_TABLE_PROPERTY_INDEX);
if (sideData == kEmptyFixedArray) {
// No side data (maybe the context was created while the side data
// collection was disabled).
return val;
}
const sideDataIndex = index - ContextSlot::MIN_CONTEXT_EXTENDED_SLOTS;
const sideDataFixedArray: FixedArray =
Cast<FixedArray>(sideData) otherwise unreachable;
let property: intptr;
typeswitch (sideDataFixedArray.objects[sideDataIndex]) {
case (Undefined): {
return val;
}
case (cell: ContextSidePropertyCell): {
property = SmiUntag(cell.property_details_raw);
}
case (smi_property: Smi): {
property = SmiUntag(smi_property);
}
case (Object): {
unreachable;
}
}
if (property == kContextSidePropertyInt32) {
const number = Cast<HeapNumber>(val) otherwise unreachable;
const int32Value = LoadHeapInt32Value(number);
return AllocateHeapNumberWithValue(ChangeInt32ToFloat64(int32Value));
} else if (property == kContextSidePropertyHeapNumber) {
const number = Cast<HeapNumber>(val) otherwise unreachable;
return AllocateHeapNumberWithValue(number.value);
} else {
return val;
}
}
// A dummy used instead of a context constant for runtime calls that don't need
// a context.
type NoContext extends Smi;
extern macro NoContextConstant(): NoContext;
const kNoContext: NoContext = NoContextConstant();