[torque] Move Map layout definition to Torque
This commit attempts to change as little behavior as possible, but it does require reordering the fields within Map to abide by Torque rules specifying that strong and weak fields must be in separate sections. Also includes some Torque compiler updates: - Allow enums (types extending from integral types) as class fields - Rename @ifdef to @if and add @ifnot for inverse checks - Allow void fields in class declarations, which take up no space and emit no accessors Bug: v8:8952 Change-Id: I1de6f34c1b15ed87d718666a05176980a218e97c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1480919 Commit-Queue: Seth Brenith <seth.brenith@microsoft.com> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#61588}
This commit is contained in:
parent
a9eaf66316
commit
15a7e04eec
@ -166,8 +166,6 @@ type DirectString extends String;
|
|||||||
|
|
||||||
type RootIndex generates 'TNode<Int32T>' constexpr 'RootIndex';
|
type RootIndex generates 'TNode<Int32T>' constexpr 'RootIndex';
|
||||||
|
|
||||||
type Map extends HeapObject generates 'TNode<Map>';
|
|
||||||
|
|
||||||
@abstract
|
@abstract
|
||||||
@noVerifier
|
@noVerifier
|
||||||
extern class FixedArrayBase extends HeapObject {
|
extern class FixedArrayBase extends HeapObject {
|
||||||
@ -184,6 +182,39 @@ extern class WeakFixedArray extends HeapObject { length: Smi; }
|
|||||||
|
|
||||||
extern class ByteArray extends FixedArrayBase {}
|
extern class ByteArray extends FixedArrayBase {}
|
||||||
|
|
||||||
|
type LayoutDescriptor extends ByteArray
|
||||||
|
generates 'TNode<LayoutDescriptor>';
|
||||||
|
type TransitionArray extends WeakFixedArray
|
||||||
|
generates 'TNode<TransitionArray>';
|
||||||
|
|
||||||
|
// InstanceType actually extends uint16, but a bunch of methods in
|
||||||
|
// CodeStubAssembler expect a TNode<Int32T>, so keeping it signed for now.
|
||||||
|
type InstanceType extends int16 constexpr 'InstanceType';
|
||||||
|
|
||||||
|
extern class Map extends HeapObject {
|
||||||
|
instance_size_in_words: uint8;
|
||||||
|
in_object_properties_start_or_constructor_function_index: uint8;
|
||||||
|
used_or_unused_instance_size_in_words: uint8;
|
||||||
|
visitor_id: uint8;
|
||||||
|
instance_type: InstanceType;
|
||||||
|
bit_field: uint8;
|
||||||
|
bit_field2: uint8;
|
||||||
|
bit_field3: uint32;
|
||||||
|
|
||||||
|
@if(TAGGED_SIZE_8_BYTES) optional_padding: uint32;
|
||||||
|
@ifnot(TAGGED_SIZE_8_BYTES) optional_padding: void;
|
||||||
|
|
||||||
|
prototype: HeapObject;
|
||||||
|
constructor_or_back_pointer: Object;
|
||||||
|
instance_descriptors: DescriptorArray;
|
||||||
|
@if(V8_DOUBLE_FIELDS_UNBOXING) layout_descriptor: LayoutDescriptor;
|
||||||
|
@ifnot(V8_DOUBLE_FIELDS_UNBOXING) layout_descriptor: void;
|
||||||
|
dependent_code: DependentCode;
|
||||||
|
prototype_validity_cell: Smi | Cell;
|
||||||
|
weak transitions_or_prototype_info: Map | TransitionArray |
|
||||||
|
PrototypeInfo | Smi;
|
||||||
|
}
|
||||||
|
|
||||||
type BytecodeArray extends FixedArrayBase;
|
type BytecodeArray extends FixedArrayBase;
|
||||||
|
|
||||||
@generatePrint
|
@generatePrint
|
||||||
@ -483,7 +514,7 @@ extern class SharedFunctionInfo extends HeapObject {
|
|||||||
expected_nof_properties: uint16;
|
expected_nof_properties: uint16;
|
||||||
function_token_offset: int16;
|
function_token_offset: int16;
|
||||||
flags: int32;
|
flags: int32;
|
||||||
@ifdef(V8_SFI_HAS_UNIQUE_ID) unique_id: int32;
|
@if(V8_SFI_HAS_UNIQUE_ID) unique_id: int32;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern class JSBoundFunction extends JSObject {
|
extern class JSBoundFunction extends JSObject {
|
||||||
@ -703,7 +734,6 @@ extern class PropertyCell extends HeapObject {
|
|||||||
|
|
||||||
extern class JSDataView extends JSArrayBufferView {}
|
extern class JSDataView extends JSArrayBufferView {}
|
||||||
|
|
||||||
type InstanceType generates 'TNode<Int32T>' constexpr 'InstanceType';
|
|
||||||
type ElementsKind generates 'TNode<Int32T>' constexpr 'ElementsKind';
|
type ElementsKind generates 'TNode<Int32T>' constexpr 'ElementsKind';
|
||||||
type LanguageMode extends Smi constexpr 'LanguageMode';
|
type LanguageMode extends Smi constexpr 'LanguageMode';
|
||||||
type ExtractFixedArrayFlags
|
type ExtractFixedArrayFlags
|
||||||
@ -2300,8 +2330,6 @@ operator '[]=' macro StoreFixedArrayDirect(a: FixedArray, i: Smi, v: Object) {
|
|||||||
a.objects[i] = v;
|
a.objects[i] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern operator '.instance_type' macro LoadMapInstanceType(Map): int32;
|
|
||||||
|
|
||||||
extern macro GetNumberDictionaryNumberOfElements(NumberDictionary): Smi;
|
extern macro GetNumberDictionaryNumberOfElements(NumberDictionary): Smi;
|
||||||
extern macro GetIteratorMethod(implicit context: Context)(HeapObject): Object
|
extern macro GetIteratorMethod(implicit context: Context)(HeapObject): Object
|
||||||
labels IfIteratorUndefined;
|
labels IfIteratorUndefined;
|
||||||
|
@ -1607,7 +1607,7 @@ TNode<Int32T> CodeStubAssembler::LoadElementsKind(
|
|||||||
TNode<DescriptorArray> CodeStubAssembler::LoadMapDescriptors(
|
TNode<DescriptorArray> CodeStubAssembler::LoadMapDescriptors(
|
||||||
SloppyTNode<Map> map) {
|
SloppyTNode<Map> map) {
|
||||||
CSA_SLOW_ASSERT(this, IsMap(map));
|
CSA_SLOW_ASSERT(this, IsMap(map));
|
||||||
return CAST(LoadObjectField(map, Map::kDescriptorsOffset));
|
return CAST(LoadObjectField(map, Map::kInstanceDescriptorsOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<HeapObject> CodeStubAssembler::LoadMapPrototype(SloppyTNode<Map> map) {
|
TNode<HeapObject> CodeStubAssembler::LoadMapPrototype(SloppyTNode<Map> map) {
|
||||||
|
@ -607,7 +607,7 @@ FieldAccess AccessBuilder::ForMapBitField3() {
|
|||||||
// static
|
// static
|
||||||
FieldAccess AccessBuilder::ForMapDescriptors() {
|
FieldAccess AccessBuilder::ForMapDescriptors() {
|
||||||
FieldAccess access = {
|
FieldAccess access = {
|
||||||
kTaggedBase, Map::kDescriptorsOffset,
|
kTaggedBase, Map::kInstanceDescriptorsOffset,
|
||||||
Handle<Name>(), MaybeHandle<Map>(),
|
Handle<Name>(), MaybeHandle<Map>(),
|
||||||
Type::OtherInternal(), MachineType::TypeCompressedTaggedPointer(),
|
Type::OtherInternal(), MachineType::TypeCompressedTaggedPointer(),
|
||||||
kPointerWriteBarrier};
|
kPointerWriteBarrier};
|
||||||
|
@ -82,6 +82,14 @@ constexpr int kStackSpaceRequiredForCompilation = 40;
|
|||||||
#define V8_DOUBLE_FIELDS_UNBOXING false
|
#define V8_DOUBLE_FIELDS_UNBOXING false
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Determine whether tagged pointers are 8 bytes (used in Torque layouts for
|
||||||
|
// choosing where to insert padding).
|
||||||
|
#if V8_TARGET_ARCH_64_BIT && !defined(V8_COMPRESS_POINTERS)
|
||||||
|
#define TAGGED_SIZE_8_BYTES true
|
||||||
|
#else
|
||||||
|
#define TAGGED_SIZE_8_BYTES false
|
||||||
|
#endif
|
||||||
|
|
||||||
// Some types of tracing require the SFI to store a unique ID.
|
// Some types of tracing require the SFI to store a unique ID.
|
||||||
#if defined(V8_TRACE_MAPS) || defined(V8_TRACE_IGNITION)
|
#if defined(V8_TRACE_MAPS) || defined(V8_TRACE_IGNITION)
|
||||||
#define V8_SFI_HAS_UNIQUE_ID true
|
#define V8_SFI_HAS_UNIQUE_ID true
|
||||||
@ -234,6 +242,7 @@ using AtomicTagged_t = base::AtomicWord;
|
|||||||
constexpr bool kUseBranchlessPtrDecompression = true;
|
constexpr bool kUseBranchlessPtrDecompression = true;
|
||||||
|
|
||||||
STATIC_ASSERT(kTaggedSize == (1 << kTaggedSizeLog2));
|
STATIC_ASSERT(kTaggedSize == (1 << kTaggedSizeLog2));
|
||||||
|
STATIC_ASSERT((kTaggedSize == 8) == TAGGED_SIZE_8_BYTES);
|
||||||
|
|
||||||
using AsAtomicTagged = base::AsAtomicPointerImpl<AtomicTagged_t>;
|
using AsAtomicTagged = base::AsAtomicPointerImpl<AtomicTagged_t>;
|
||||||
STATIC_ASSERT(sizeof(Tagged_t) == kTaggedSize);
|
STATIC_ASSERT(sizeof(Tagged_t) == kTaggedSize);
|
||||||
|
@ -714,18 +714,16 @@ class WasmInstanceObject::BodyDescriptor final : public BodyDescriptorBase {
|
|||||||
class Map::BodyDescriptor final : public BodyDescriptorBase {
|
class Map::BodyDescriptor final : public BodyDescriptorBase {
|
||||||
public:
|
public:
|
||||||
static bool IsValidSlot(Map map, HeapObject obj, int offset) {
|
static bool IsValidSlot(Map map, HeapObject obj, int offset) {
|
||||||
return offset >= Map::kPointerFieldsBeginOffset &&
|
return offset >= Map::kStartOfPointerFieldsOffset &&
|
||||||
offset < Map::kPointerFieldsEndOffset;
|
offset < Map::kEndOfTaggedFieldsOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ObjectVisitor>
|
template <typename ObjectVisitor>
|
||||||
static inline void IterateBody(Map map, HeapObject obj, int object_size,
|
static inline void IterateBody(Map map, HeapObject obj, int object_size,
|
||||||
ObjectVisitor* v) {
|
ObjectVisitor* v) {
|
||||||
IteratePointers(obj, Map::kPointerFieldsBeginOffset,
|
IteratePointers(obj, Map::kStartOfStrongFieldsOffset,
|
||||||
Map::kTransitionsOrPrototypeInfoOffset, v);
|
Map::kEndOfStrongFieldsOffset, v);
|
||||||
IterateMaybeWeakPointer(obj, kTransitionsOrPrototypeInfoOffset, v);
|
IterateMaybeWeakPointer(obj, kTransitionsOrPrototypeInfoOffset, v);
|
||||||
IteratePointers(obj, Map::kTransitionsOrPrototypeInfoOffset + kTaggedSize,
|
|
||||||
Map::kPointerFieldsEndOffset, v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int SizeOf(Map map, HeapObject obj) { return Map::kSize; }
|
static inline int SizeOf(Map map, HeapObject obj) { return Map::kSize; }
|
||||||
|
@ -659,6 +659,7 @@ void JSObject::JSObjectVerify(Isolate* isolate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Map::MapVerify(Isolate* isolate) {
|
void Map::MapVerify(Isolate* isolate) {
|
||||||
|
TorqueGeneratedClassVerifiers::MapVerify(*this, isolate);
|
||||||
Heap* heap = isolate->heap();
|
Heap* heap = isolate->heap();
|
||||||
CHECK(!ObjectInYoungGeneration(*this));
|
CHECK(!ObjectInYoungGeneration(*this));
|
||||||
CHECK(FIRST_TYPE <= instance_type() && instance_type() <= LAST_TYPE);
|
CHECK(FIRST_TYPE <= instance_type() && instance_type() <= LAST_TYPE);
|
||||||
@ -667,8 +668,6 @@ void Map::MapVerify(Isolate* isolate) {
|
|||||||
static_cast<size_t>(instance_size()) < heap->Capacity()));
|
static_cast<size_t>(instance_size()) < heap->Capacity()));
|
||||||
CHECK(GetBackPointer()->IsUndefined(isolate) ||
|
CHECK(GetBackPointer()->IsUndefined(isolate) ||
|
||||||
!Map::cast(GetBackPointer())->is_stable());
|
!Map::cast(GetBackPointer())->is_stable());
|
||||||
HeapObject::VerifyHeapPointer(isolate, prototype());
|
|
||||||
HeapObject::VerifyHeapPointer(isolate, instance_descriptors());
|
|
||||||
SLOW_DCHECK(instance_descriptors()->IsSortedNoDuplicates());
|
SLOW_DCHECK(instance_descriptors()->IsSortedNoDuplicates());
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
SLOW_DCHECK(
|
SLOW_DCHECK(
|
||||||
@ -698,8 +697,6 @@ void Map::MapVerify(Isolate* isolate) {
|
|||||||
DCHECK(prototype_info() == Smi::kZero ||
|
DCHECK(prototype_info() == Smi::kZero ||
|
||||||
prototype_info()->IsPrototypeInfo());
|
prototype_info()->IsPrototypeInfo());
|
||||||
}
|
}
|
||||||
CHECK(prototype_validity_cell()->IsSmi() ||
|
|
||||||
prototype_validity_cell()->IsCell());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::DictionaryMapVerify(Isolate* isolate) {
|
void Map::DictionaryMapVerify(Isolate* isolate) {
|
||||||
|
@ -387,6 +387,10 @@ STATIC_ASSERT(FIRST_NONSTRING_TYPE == Internals::kFirstNonstringType);
|
|||||||
STATIC_ASSERT(ODDBALL_TYPE == Internals::kOddballType);
|
STATIC_ASSERT(ODDBALL_TYPE == Internals::kOddballType);
|
||||||
STATIC_ASSERT(FOREIGN_TYPE == Internals::kForeignType);
|
STATIC_ASSERT(FOREIGN_TYPE == Internals::kForeignType);
|
||||||
|
|
||||||
|
// Make sure it doesn't matter whether we sign-extend or zero-extend these
|
||||||
|
// values, because Torque treats InstanceType as signed.
|
||||||
|
STATIC_ASSERT(LAST_TYPE < 1 << 15);
|
||||||
|
|
||||||
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
|
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
|
||||||
InstanceType instance_type);
|
InstanceType instance_type);
|
||||||
|
|
||||||
|
@ -31,17 +31,18 @@ OBJECT_CONSTRUCTORS_IMPL(Map, HeapObject)
|
|||||||
CAST_ACCESSOR(Map)
|
CAST_ACCESSOR(Map)
|
||||||
|
|
||||||
DescriptorArray Map::instance_descriptors() const {
|
DescriptorArray Map::instance_descriptors() const {
|
||||||
return DescriptorArray::cast(READ_FIELD(*this, kDescriptorsOffset));
|
return DescriptorArray::cast(READ_FIELD(*this, kInstanceDescriptorsOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorArray Map::synchronized_instance_descriptors() const {
|
DescriptorArray Map::synchronized_instance_descriptors() const {
|
||||||
return DescriptorArray::cast(ACQUIRE_READ_FIELD(*this, kDescriptorsOffset));
|
return DescriptorArray::cast(
|
||||||
|
ACQUIRE_READ_FIELD(*this, kInstanceDescriptorsOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::set_synchronized_instance_descriptors(DescriptorArray value,
|
void Map::set_synchronized_instance_descriptors(DescriptorArray value,
|
||||||
WriteBarrierMode mode) {
|
WriteBarrierMode mode) {
|
||||||
RELEASE_WRITE_FIELD(*this, kDescriptorsOffset, value);
|
RELEASE_WRITE_FIELD(*this, kInstanceDescriptorsOffset, value);
|
||||||
CONDITIONAL_WRITE_BARRIER(*this, kDescriptorsOffset, value, mode);
|
CONDITIONAL_WRITE_BARRIER(*this, kInstanceDescriptorsOffset, value, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A freshly allocated layout descriptor can be set on an existing map.
|
// A freshly allocated layout descriptor can be set on an existing map.
|
||||||
|
@ -1497,26 +1497,20 @@ Handle<Map> Map::Normalize(Isolate* isolate, Handle<Map> fast_map,
|
|||||||
// applied to the shared map, dependent code and weak cell cache.
|
// applied to the shared map, dependent code and weak cell cache.
|
||||||
Handle<Map> fresh = Map::CopyNormalized(isolate, fast_map, mode);
|
Handle<Map> fresh = Map::CopyNormalized(isolate, fast_map, mode);
|
||||||
|
|
||||||
if (new_map->is_prototype_map()) {
|
|
||||||
// For prototype maps, the PrototypeInfo is not copied.
|
|
||||||
DCHECK_EQ(0, memcmp(reinterpret_cast<void*>(fresh->address()),
|
|
||||||
reinterpret_cast<void*>(new_map->address()),
|
|
||||||
kTransitionsOrPrototypeInfoOffset));
|
|
||||||
DCHECK_EQ(fresh->raw_transitions(),
|
|
||||||
MaybeObject::FromObject(Smi::kZero));
|
|
||||||
STATIC_ASSERT(kDescriptorsOffset ==
|
|
||||||
kTransitionsOrPrototypeInfoOffset + kTaggedSize);
|
|
||||||
DCHECK_EQ(0, memcmp(fresh->RawField(kDescriptorsOffset).ToVoidPtr(),
|
|
||||||
new_map->RawField(kDescriptorsOffset).ToVoidPtr(),
|
|
||||||
kDependentCodeOffset - kDescriptorsOffset));
|
|
||||||
} else {
|
|
||||||
DCHECK_EQ(0, memcmp(reinterpret_cast<void*>(fresh->address()),
|
|
||||||
reinterpret_cast<void*>(new_map->address()),
|
|
||||||
Map::kDependentCodeOffset));
|
|
||||||
}
|
|
||||||
STATIC_ASSERT(Map::kPrototypeValidityCellOffset ==
|
STATIC_ASSERT(Map::kPrototypeValidityCellOffset ==
|
||||||
Map::kDependentCodeOffset + kTaggedSize);
|
Map::kDependentCodeOffset + kTaggedSize);
|
||||||
|
DCHECK_EQ(0, memcmp(reinterpret_cast<void*>(fresh->address()),
|
||||||
|
reinterpret_cast<void*>(new_map->address()),
|
||||||
|
Map::kDependentCodeOffset));
|
||||||
int offset = Map::kPrototypeValidityCellOffset + kTaggedSize;
|
int offset = Map::kPrototypeValidityCellOffset + kTaggedSize;
|
||||||
|
if (new_map->is_prototype_map()) {
|
||||||
|
// For prototype maps, the PrototypeInfo is not copied.
|
||||||
|
STATIC_ASSERT(Map::kTransitionsOrPrototypeInfoOffset ==
|
||||||
|
Map::kPrototypeValidityCellOffset + kTaggedSize);
|
||||||
|
offset = kTransitionsOrPrototypeInfoOffset + kTaggedSize;
|
||||||
|
DCHECK_EQ(fresh->raw_transitions(),
|
||||||
|
MaybeObject::FromObject(Smi::kZero));
|
||||||
|
}
|
||||||
DCHECK_EQ(0, memcmp(reinterpret_cast<void*>(fresh->address() + offset),
|
DCHECK_EQ(0, memcmp(reinterpret_cast<void*>(fresh->address() + offset),
|
||||||
reinterpret_cast<void*>(new_map->address() + offset),
|
reinterpret_cast<void*>(new_map->address() + offset),
|
||||||
Map::kSize - offset));
|
Map::kSize - offset));
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "src/objects.h"
|
#include "src/objects.h"
|
||||||
#include "src/objects/code.h"
|
#include "src/objects/code.h"
|
||||||
#include "src/objects/heap-object.h"
|
#include "src/objects/heap-object.h"
|
||||||
|
#include "torque-generated/field-offsets-tq.h"
|
||||||
|
|
||||||
// Has to be the last include (doesn't have include guards):
|
// Has to be the last include (doesn't have include guards):
|
||||||
#include "src/objects/object-macros.h"
|
#include "src/objects/object-macros.h"
|
||||||
@ -166,11 +167,6 @@ using MapHandles = std::vector<Handle<Map>>;
|
|||||||
// +---------------+---------------------------------------------+
|
// +---------------+---------------------------------------------+
|
||||||
// | TaggedPointer | [constructor_or_backpointer] |
|
// | TaggedPointer | [constructor_or_backpointer] |
|
||||||
// +---------------+---------------------------------------------+
|
// +---------------+---------------------------------------------+
|
||||||
// | TaggedPointer | If Map is a prototype map: |
|
|
||||||
// | | [prototype_info] |
|
|
||||||
// | | Else: |
|
|
||||||
// | | [raw_transitions] |
|
|
||||||
// +---------------+---------------------------------------------+
|
|
||||||
// | TaggedPointer | [instance_descriptors] |
|
// | TaggedPointer | [instance_descriptors] |
|
||||||
// +*************************************************************+
|
// +*************************************************************+
|
||||||
// ! TaggedPointer ! [layout_descriptors] !
|
// ! TaggedPointer ! [layout_descriptors] !
|
||||||
@ -180,6 +176,13 @@ using MapHandles = std::vector<Handle<Map>>;
|
|||||||
// +*************************************************************+
|
// +*************************************************************+
|
||||||
// | TaggedPointer | [dependent_code] |
|
// | TaggedPointer | [dependent_code] |
|
||||||
// +---------------+---------------------------------------------+
|
// +---------------+---------------------------------------------+
|
||||||
|
// | TaggedPointer | [prototype_validity_cell] |
|
||||||
|
// +---------------+---------------------------------------------+
|
||||||
|
// | TaggedPointer | If Map is a prototype map: |
|
||||||
|
// | | [prototype_info] |
|
||||||
|
// | | Else: |
|
||||||
|
// | | [raw_transitions] |
|
||||||
|
// +---------------+---------------------------------------------+
|
||||||
|
|
||||||
class Map : public HeapObject {
|
class Map : public HeapObject {
|
||||||
public:
|
public:
|
||||||
@ -828,34 +831,8 @@ class Map : public HeapObject {
|
|||||||
|
|
||||||
static const int kMaxPreAllocatedPropertyFields = 255;
|
static const int kMaxPreAllocatedPropertyFields = 255;
|
||||||
|
|
||||||
// Layout description.
|
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
|
||||||
#define MAP_FIELDS(V) \
|
TORQUE_GENERATED_MAP_FIELDS)
|
||||||
/* Raw data fields. */ \
|
|
||||||
V(kInstanceSizeInWordsOffset, kUInt8Size) \
|
|
||||||
V(kInObjectPropertiesStartOrConstructorFunctionIndexOffset, kUInt8Size) \
|
|
||||||
V(kUsedOrUnusedInstanceSizeInWordsOffset, kUInt8Size) \
|
|
||||||
V(kVisitorIdOffset, kUInt8Size) \
|
|
||||||
V(kInstanceTypeOffset, kUInt16Size) \
|
|
||||||
V(kBitFieldOffset, kUInt8Size) \
|
|
||||||
V(kBitField2Offset, kUInt8Size) \
|
|
||||||
V(kBitField3Offset, kUInt32Size) \
|
|
||||||
/* Adds padding to make tagged fields kTaggedSize-aligned. */ \
|
|
||||||
V(kOptionalPaddingOffset, OBJECT_POINTER_PADDING(kOptionalPaddingOffset)) \
|
|
||||||
/* Pointer fields. */ \
|
|
||||||
V(kPointerFieldsBeginOffset, 0) \
|
|
||||||
V(kPrototypeOffset, kTaggedSize) \
|
|
||||||
V(kConstructorOrBackPointerOffset, kTaggedSize) \
|
|
||||||
V(kTransitionsOrPrototypeInfoOffset, kTaggedSize) \
|
|
||||||
V(kDescriptorsOffset, kTaggedSize) \
|
|
||||||
V(kLayoutDescriptorOffset, FLAG_unbox_double_fields ? kTaggedSize : 0) \
|
|
||||||
V(kDependentCodeOffset, kTaggedSize) \
|
|
||||||
V(kPrototypeValidityCellOffset, kTaggedSize) \
|
|
||||||
V(kPointerFieldsEndOffset, 0) \
|
|
||||||
/* Total size. */ \
|
|
||||||
V(kSize, 0)
|
|
||||||
|
|
||||||
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, MAP_FIELDS)
|
|
||||||
#undef MAP_FIELDS
|
|
||||||
|
|
||||||
STATIC_ASSERT(kInstanceTypeOffset == Internals::kMapInstanceTypeOffset);
|
STATIC_ASSERT(kInstanceTypeOffset == Internals::kMapInstanceTypeOffset);
|
||||||
|
|
||||||
|
@ -1025,7 +1025,7 @@ void V8HeapExplorer::ExtractMapReferences(HeapEntry* entry, Map map) {
|
|||||||
DescriptorArray descriptors = map->instance_descriptors();
|
DescriptorArray descriptors = map->instance_descriptors();
|
||||||
TagObject(descriptors, "(map descriptors)");
|
TagObject(descriptors, "(map descriptors)");
|
||||||
SetInternalReference(entry, "descriptors", descriptors,
|
SetInternalReference(entry, "descriptors", descriptors,
|
||||||
Map::kDescriptorsOffset);
|
Map::kInstanceDescriptorsOffset);
|
||||||
SetInternalReference(entry, "prototype", map->prototype(),
|
SetInternalReference(entry, "prototype", map->prototype(),
|
||||||
Map::kPrototypeOffset);
|
Map::kPrototypeOffset);
|
||||||
if (FLAG_unbox_double_fields) {
|
if (FLAG_unbox_double_fields) {
|
||||||
|
@ -715,10 +715,20 @@ struct StructFieldExpression {
|
|||||||
bool const_qualified;
|
bool const_qualified;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ConditionalAnnotationType {
|
||||||
|
kPositive,
|
||||||
|
kNegative,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConditionalAnnotation {
|
||||||
|
std::string condition;
|
||||||
|
ConditionalAnnotationType type;
|
||||||
|
};
|
||||||
|
|
||||||
struct ClassFieldExpression {
|
struct ClassFieldExpression {
|
||||||
NameAndTypeExpression name_and_type;
|
NameAndTypeExpression name_and_type;
|
||||||
base::Optional<std::string> index;
|
base::Optional<std::string> index;
|
||||||
base::Optional<std::string> conditional;
|
base::Optional<ConditionalAnnotation> conditional;
|
||||||
bool weak;
|
bool weak;
|
||||||
bool const_qualified;
|
bool const_qualified;
|
||||||
bool generate_verify;
|
bool generate_verify;
|
||||||
|
@ -54,6 +54,8 @@ enum class ParseResultHolderBase::TypeId {
|
|||||||
kOptionalLabelBlockPtr,
|
kOptionalLabelBlockPtr,
|
||||||
kNameAndTypeExpression,
|
kNameAndTypeExpression,
|
||||||
kNameAndExpression,
|
kNameAndExpression,
|
||||||
|
kConditionalAnnotation,
|
||||||
|
kOptionalConditionalAnnotation,
|
||||||
kClassFieldExpression,
|
kClassFieldExpression,
|
||||||
kStructFieldExpression,
|
kStructFieldExpression,
|
||||||
kStdVectorOfNameAndTypeExpression,
|
kStdVectorOfNameAndTypeExpression,
|
||||||
|
@ -3097,7 +3097,7 @@ void ImplementationVisitor::GenerateClassFieldOffsets(
|
|||||||
FieldSectionType::kStrongSection,
|
FieldSectionType::kStrongSection,
|
||||||
&new_contents_stream);
|
&new_contents_stream);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (f.name_and_type.type != TypeOracle::GetVoidType()) {
|
||||||
ProcessFieldInSection(§ion, &completed_sections,
|
ProcessFieldInSection(§ion, &completed_sections,
|
||||||
FieldSectionType::kScalarSection,
|
FieldSectionType::kScalarSection,
|
||||||
&new_contents_stream);
|
&new_contents_stream);
|
||||||
@ -3105,8 +3105,7 @@ void ImplementationVisitor::GenerateClassFieldOffsets(
|
|||||||
size_t field_size;
|
size_t field_size;
|
||||||
std::string size_string;
|
std::string size_string;
|
||||||
std::string machine_type;
|
std::string machine_type;
|
||||||
std::tie(field_size, size_string, machine_type) =
|
std::tie(field_size, size_string) = f.GetFieldSizeInformation();
|
||||||
f.GetFieldSizeInformation();
|
|
||||||
new_contents_stream << "V(k" << CamelifyString(f.name_and_type.name)
|
new_contents_stream << "V(k" << CamelifyString(f.name_and_type.name)
|
||||||
<< "Offset, " << size_string << ") \\\n";
|
<< "Offset, " << size_string << ") \\\n";
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,8 @@ class BuildFlags : public ContextualClass<BuildFlags> {
|
|||||||
public:
|
public:
|
||||||
BuildFlags() {
|
BuildFlags() {
|
||||||
build_flags_["V8_SFI_HAS_UNIQUE_ID"] = V8_SFI_HAS_UNIQUE_ID;
|
build_flags_["V8_SFI_HAS_UNIQUE_ID"] = V8_SFI_HAS_UNIQUE_ID;
|
||||||
|
build_flags_["TAGGED_SIZE_8_BYTES"] = TAGGED_SIZE_8_BYTES;
|
||||||
|
build_flags_["V8_DOUBLE_FIELDS_UNBOXING"] = V8_DOUBLE_FIELDS_UNBOXING;
|
||||||
build_flags_["TRUE_FOR_TESTING"] = true;
|
build_flags_["TRUE_FOR_TESTING"] = true;
|
||||||
build_flags_["FALSE_FOR_TESTING"] = false;
|
build_flags_["FALSE_FOR_TESTING"] = false;
|
||||||
}
|
}
|
||||||
@ -105,6 +107,14 @@ V8_EXPORT_PRIVATE const ParseResultTypeId
|
|||||||
ParseResultHolder<NameAndExpression>::id =
|
ParseResultHolder<NameAndExpression>::id =
|
||||||
ParseResultTypeId::kNameAndExpression;
|
ParseResultTypeId::kNameAndExpression;
|
||||||
template <>
|
template <>
|
||||||
|
V8_EXPORT_PRIVATE const ParseResultTypeId
|
||||||
|
ParseResultHolder<ConditionalAnnotation>::id =
|
||||||
|
ParseResultTypeId::kConditionalAnnotation;
|
||||||
|
template <>
|
||||||
|
V8_EXPORT_PRIVATE const ParseResultTypeId
|
||||||
|
ParseResultHolder<base::Optional<ConditionalAnnotation>>::id =
|
||||||
|
ParseResultTypeId::kOptionalConditionalAnnotation;
|
||||||
|
template <>
|
||||||
V8_EXPORT_PRIVATE const ParseResultTypeId
|
V8_EXPORT_PRIVATE const ParseResultTypeId
|
||||||
ParseResultHolder<ClassFieldExpression>::id =
|
ParseResultHolder<ClassFieldExpression>::id =
|
||||||
ParseResultTypeId::kClassFieldExpression;
|
ParseResultTypeId::kClassFieldExpression;
|
||||||
@ -704,8 +714,12 @@ base::Optional<ParseResult> MakeClassDeclaration(
|
|||||||
std::vector<ClassFieldExpression> fields;
|
std::vector<ClassFieldExpression> fields;
|
||||||
std::copy_if(fields_raw.begin(), fields_raw.end(), std::back_inserter(fields),
|
std::copy_if(fields_raw.begin(), fields_raw.end(), std::back_inserter(fields),
|
||||||
[](const ClassFieldExpression& exp) {
|
[](const ClassFieldExpression& exp) {
|
||||||
return !exp.conditional.has_value() ||
|
if (!exp.conditional.has_value()) return true;
|
||||||
BuildFlags::GetFlag(*exp.conditional, "@ifdef");
|
const ConditionalAnnotation& conditional = *exp.conditional;
|
||||||
|
return conditional.type == ConditionalAnnotationType::kPositive
|
||||||
|
? BuildFlags::GetFlag(conditional.condition, "@if")
|
||||||
|
: !BuildFlags::GetFlag(conditional.condition,
|
||||||
|
"@ifnot");
|
||||||
});
|
});
|
||||||
|
|
||||||
Declaration* result = MakeNode<ClassDeclaration>(
|
Declaration* result = MakeNode<ClassDeclaration>(
|
||||||
@ -1284,8 +1298,20 @@ base::Optional<ParseResult> MakeNameAndExpressionFromExpression(
|
|||||||
ReportError("Constructor parameters need to be named.");
|
ReportError("Constructor parameters need to be named.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base::Optional<ParseResult> MakeConditionalAnnotation(
|
||||||
|
ParseResultIterator* child_results) {
|
||||||
|
auto type_str = child_results->NextAs<Identifier*>()->value;
|
||||||
|
DCHECK(type_str == "@if" || type_str == "@ifnot");
|
||||||
|
ConditionalAnnotationType type = type_str == "@if"
|
||||||
|
? ConditionalAnnotationType::kPositive
|
||||||
|
: ConditionalAnnotationType::kNegative;
|
||||||
|
auto condition = child_results->NextAs<std::string>();
|
||||||
|
return ParseResult{ConditionalAnnotation{condition, type}};
|
||||||
|
}
|
||||||
|
|
||||||
base::Optional<ParseResult> MakeClassField(ParseResultIterator* child_results) {
|
base::Optional<ParseResult> MakeClassField(ParseResultIterator* child_results) {
|
||||||
auto conditional = child_results->NextAs<base::Optional<std::string>>();
|
auto conditional =
|
||||||
|
child_results->NextAs<base::Optional<ConditionalAnnotation>>();
|
||||||
AnnotationSet annotations(child_results, {"@noVerifier"});
|
AnnotationSet annotations(child_results, {"@noVerifier"});
|
||||||
bool generate_verify = !annotations.Contains("@noVerifier");
|
bool generate_verify = !annotations.Contains("@noVerifier");
|
||||||
auto weak = child_results->NextAs<bool>();
|
auto weak = child_results->NextAs<bool>();
|
||||||
@ -1516,12 +1542,16 @@ struct TorqueGrammar : Grammar {
|
|||||||
Symbol* optionalArraySpecifier =
|
Symbol* optionalArraySpecifier =
|
||||||
Optional<std::string>(Sequence({Token("["), &identifier, Token("]")}));
|
Optional<std::string>(Sequence({Token("["), &identifier, Token("]")}));
|
||||||
|
|
||||||
Symbol classField = {Rule(
|
// Result: ConditionalAnnotation
|
||||||
{Optional<std::string>(
|
Symbol conditionalAnnotation = {
|
||||||
Sequence({Token("@ifdef"), Token("("), &identifier, Token(")")})),
|
Rule({OneOf({"@if", "@ifnot"}), Token("("), &identifier, Token(")")},
|
||||||
annotations, CheckIf(Token("weak")), CheckIf(Token("const")), &name,
|
MakeConditionalAnnotation)};
|
||||||
optionalArraySpecifier, Token(":"), &type, Token(";")},
|
|
||||||
MakeClassField)};
|
Symbol classField = {
|
||||||
|
Rule({Optional<ConditionalAnnotation>(&conditionalAnnotation),
|
||||||
|
annotations, CheckIf(Token("weak")), CheckIf(Token("const")), &name,
|
||||||
|
optionalArraySpecifier, Token(":"), &type, Token(";")},
|
||||||
|
MakeClassField)};
|
||||||
|
|
||||||
Symbol structField = {
|
Symbol structField = {
|
||||||
Rule({CheckIf(Token("const")), &name, Token(":"), &type, Token(";")},
|
Rule({CheckIf(Token("const")), &name, Token(":"), &type, Token(";")},
|
||||||
|
@ -269,11 +269,10 @@ void TypeVisitor::VisitClassFieldsAndMethods(
|
|||||||
size_t field_size;
|
size_t field_size;
|
||||||
std::string size_string;
|
std::string size_string;
|
||||||
std::string machine_type;
|
std::string machine_type;
|
||||||
std::tie(field_size, size_string, machine_type) =
|
std::tie(field_size, size_string) = field.GetFieldSizeInformation();
|
||||||
field.GetFieldSizeInformation();
|
|
||||||
// Our allocations don't support alignments beyond kTaggedSize.
|
// Our allocations don't support alignments beyond kTaggedSize.
|
||||||
size_t alignment = std::min(size_t{kTaggedSize}, field_size);
|
size_t alignment = std::min(size_t{kTaggedSize}, field_size);
|
||||||
if (class_offset % alignment != 0) {
|
if (alignment > 0 && class_offset % alignment != 0) {
|
||||||
ReportError("field ", field_expression.name_and_type.name,
|
ReportError("field ", field_expression.name_and_type.name,
|
||||||
" at offset ", class_offset, " is not ", alignment,
|
" at offset ", class_offset, " is not ", alignment,
|
||||||
"-byte aligned.");
|
"-byte aligned.");
|
||||||
|
@ -375,7 +375,9 @@ void ClassType::GenerateAccessors() {
|
|||||||
// function and define a corresponding '.field' operator. The
|
// function and define a corresponding '.field' operator. The
|
||||||
// implementation iterator will turn the snippets into code.
|
// implementation iterator will turn the snippets into code.
|
||||||
for (auto& field : fields_) {
|
for (auto& field : fields_) {
|
||||||
if (field.index) continue;
|
if (field.index || field.name_and_type.type == TypeOracle::GetVoidType()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
CurrentSourcePosition::Scope position_activator(field.pos);
|
CurrentSourcePosition::Scope position_activator(field.pos);
|
||||||
IdentifierExpression* parameter =
|
IdentifierExpression* parameter =
|
||||||
MakeNode<IdentifierExpression>(MakeNode<Identifier>(std::string{"o"}));
|
MakeNode<IdentifierExpression>(MakeNode<Identifier>(std::string{"o"}));
|
||||||
@ -587,62 +589,50 @@ VisitResult VisitResult::NeverResult() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<size_t, std::string, std::string> Field::GetFieldSizeInformation()
|
std::tuple<size_t, std::string> Field::GetFieldSizeInformation() const {
|
||||||
const {
|
|
||||||
std::string size_string = "#no size";
|
std::string size_string = "#no size";
|
||||||
std::string machine_type = "#no machine type";
|
|
||||||
const Type* field_type = this->name_and_type.type;
|
const Type* field_type = this->name_and_type.type;
|
||||||
size_t field_size = 0;
|
size_t field_size = 0;
|
||||||
if (field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
|
if (field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
|
||||||
field_size = kTaggedSize;
|
field_size = kTaggedSize;
|
||||||
size_string = "kTaggedSize";
|
size_string = "kTaggedSize";
|
||||||
machine_type = field_type->IsSubtypeOf(TypeOracle::GetSmiType())
|
|
||||||
? "MachineType::TaggedSigned()"
|
|
||||||
: "MachineType::AnyTagged()";
|
|
||||||
} else if (field_type->IsSubtypeOf(TypeOracle::GetRawPtrType())) {
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetRawPtrType())) {
|
||||||
field_size = kSystemPointerSize;
|
field_size = kSystemPointerSize;
|
||||||
size_string = "kSystemPointerSize";
|
size_string = "kSystemPointerSize";
|
||||||
machine_type = "MachineType::Pointer()";
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetVoidType())) {
|
||||||
} else if (field_type == TypeOracle::GetInt32Type()) {
|
field_size = 0;
|
||||||
field_size = kInt32Size;
|
size_string = "0";
|
||||||
size_string = "kInt32Size";
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetInt8Type())) {
|
||||||
machine_type = "MachineType::Int32()";
|
|
||||||
} else if (field_type == TypeOracle::GetUint32Type()) {
|
|
||||||
field_size = kInt32Size;
|
|
||||||
size_string = "kInt32Size";
|
|
||||||
machine_type = "MachineType::Uint32()";
|
|
||||||
} else if (field_type == TypeOracle::GetInt16Type()) {
|
|
||||||
field_size = kUInt16Size;
|
|
||||||
size_string = "kUInt16Size";
|
|
||||||
machine_type = "MachineType::Int16()";
|
|
||||||
} else if (field_type == TypeOracle::GetUint16Type()) {
|
|
||||||
field_size = kUInt16Size;
|
|
||||||
size_string = "kUInt16Size";
|
|
||||||
machine_type = "MachineType::Uint16()";
|
|
||||||
} else if (field_type == TypeOracle::GetInt8Type()) {
|
|
||||||
field_size = kUInt8Size;
|
field_size = kUInt8Size;
|
||||||
size_string = "kUInt8Size";
|
size_string = "kUInt8Size";
|
||||||
machine_type = "MachineType::Int8()";
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetUint8Type())) {
|
||||||
} else if (field_type == TypeOracle::GetUint8Type()) {
|
|
||||||
field_size = kUInt8Size;
|
field_size = kUInt8Size;
|
||||||
size_string = "kUInt8Size";
|
size_string = "kUInt8Size";
|
||||||
machine_type = "MachineType::Uint8()";
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetInt16Type())) {
|
||||||
} else if (field_type == TypeOracle::GetFloat64Type()) {
|
field_size = kUInt16Size;
|
||||||
|
size_string = "kUInt16Size";
|
||||||
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetUint16Type())) {
|
||||||
|
field_size = kUInt16Size;
|
||||||
|
size_string = "kUInt16Size";
|
||||||
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetInt32Type())) {
|
||||||
|
field_size = kInt32Size;
|
||||||
|
size_string = "kInt32Size";
|
||||||
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetUint32Type())) {
|
||||||
|
field_size = kInt32Size;
|
||||||
|
size_string = "kInt32Size";
|
||||||
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetFloat64Type())) {
|
||||||
field_size = kDoubleSize;
|
field_size = kDoubleSize;
|
||||||
size_string = "kDoubleSize";
|
size_string = "kDoubleSize";
|
||||||
machine_type = "MachineType::Float64()";
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetIntPtrType())) {
|
||||||
} else if (field_type == TypeOracle::GetIntPtrType()) {
|
|
||||||
field_size = kIntptrSize;
|
field_size = kIntptrSize;
|
||||||
size_string = "kIntptrSize";
|
size_string = "kIntptrSize";
|
||||||
machine_type = "MachineType::IntPtr()";
|
} else if (field_type->IsSubtypeOf(TypeOracle::GetUIntPtrType())) {
|
||||||
} else if (field_type == TypeOracle::GetUIntPtrType()) {
|
|
||||||
field_size = kIntptrSize;
|
field_size = kIntptrSize;
|
||||||
size_string = "kIntptrSize";
|
size_string = "kIntptrSize";
|
||||||
machine_type = "MachineType::IntPtr()";
|
|
||||||
} else {
|
} else {
|
||||||
ReportError("fields of type ", *field_type, " are not (yet) supported");
|
ReportError("fields of type ", *field_type, " are not (yet) supported");
|
||||||
}
|
}
|
||||||
return std::make_tuple(field_size, size_string, machine_type);
|
return std::make_tuple(field_size, size_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace torque
|
} // namespace torque
|
||||||
|
@ -146,7 +146,7 @@ struct Field {
|
|||||||
// TODO(danno): This likely should be refactored, the handling of the types
|
// TODO(danno): This likely should be refactored, the handling of the types
|
||||||
// using the universal grab-bag utility with std::tie, as well as the
|
// using the universal grab-bag utility with std::tie, as well as the
|
||||||
// reliance of string types is quite clunky.
|
// reliance of string types is quite clunky.
|
||||||
std::tuple<size_t, std::string, std::string> GetFieldSizeInformation() const;
|
std::tuple<size_t, std::string> GetFieldSizeInformation() const;
|
||||||
|
|
||||||
SourcePosition pos;
|
SourcePosition pos;
|
||||||
const AggregateType* aggregate;
|
const AggregateType* aggregate;
|
||||||
|
@ -785,14 +785,18 @@ namespace test {
|
|||||||
i: uintptr;
|
i: uintptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This class should throw alignment errors if @ifdef decorators aren't
|
// This class should throw alignment errors if @if decorators aren't
|
||||||
// working.
|
// working.
|
||||||
@noVerifier
|
@noVerifier
|
||||||
extern class PreprocessingTest extends JSObject {
|
extern class PreprocessingTest extends JSObject {
|
||||||
@ifdef(FALSE_FOR_TESTING) a: int8;
|
@if(FALSE_FOR_TESTING) a: int8;
|
||||||
@ifdef(TRUE_FOR_TESTING) a: int16;
|
@if(TRUE_FOR_TESTING) a: int16;
|
||||||
b: int16;
|
b: int16;
|
||||||
d: int32;
|
d: int32;
|
||||||
|
@ifnot(TRUE_FOR_TESTING) e: int8;
|
||||||
|
@ifnot(FALSE_FOR_TESTING) f: int16;
|
||||||
|
g: int16;
|
||||||
|
h: int32;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro TestClassWithAllTypesLoadsAndStores(
|
macro TestClassWithAllTypesLoadsAndStores(
|
||||||
|
@ -49,6 +49,7 @@ def preprocess(input):
|
|||||||
r'\n otherwise', input)
|
r'\n otherwise', input)
|
||||||
input = re.sub(r'(\n\s*\S[^\n]*\s)otherwise',
|
input = re.sub(r'(\n\s*\S[^\n]*\s)otherwise',
|
||||||
r'\1_OtheSaLi', input)
|
r'\1_OtheSaLi', input)
|
||||||
|
input = re.sub(r'@if\(', r'@iF(', input)
|
||||||
|
|
||||||
# Special handing of '%' for intrinsics, turn the percent
|
# Special handing of '%' for intrinsics, turn the percent
|
||||||
# into a unicode character so that it gets treated as part of the
|
# into a unicode character so that it gets treated as part of the
|
||||||
@ -83,6 +84,7 @@ def postprocess(output):
|
|||||||
r"\n\1otherwise", output)
|
r"\n\1otherwise", output)
|
||||||
output = re.sub(r'_OtheSaLi',
|
output = re.sub(r'_OtheSaLi',
|
||||||
r"otherwise", output)
|
r"otherwise", output)
|
||||||
|
output = re.sub(r'@iF\(', r'@if(', output)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
old = output
|
old = output
|
||||||
|
Loading…
Reference in New Issue
Block a user