All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.js.runtime.objects.JSShapeData Maven / Gradle / Ivy

/*
 * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.js.runtime.objects;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.Symbol;
import com.oracle.truffle.js.runtime.util.DebugCounter;
import com.oracle.truffle.js.runtime.util.UnmodifiableArrayList;
import com.oracle.truffle.js.runtime.util.UnmodifiablePropertyKeyList;

/**
 * Extra metadata associated with JavaScript object shapes.
 *
 * @see JSShape
 */
public final class JSShapeData {
    private static final Property[] EMPTY_PROPERTY_ARRAY = new Property[0];
    private static final TruffleString[] EMPTY_STRING_ARRAY = new TruffleString[0];
    private static final int UNKNOWN = -1;

    /** The position in the property array where strings end and symbols start. */
    private int symbolsStartPos = UNKNOWN;
    /** All properties, sorted using {@link JSRuntime#comparePropertyKeys}. */
    private Property[] propertyArray;
    /** Only enumerable properties with string keys (no symbols). */
    private TruffleString[] enumerablePropertyNames;

    private JSShapeData() {
    }

    private static Property[] createPropertiesArray(Shape shape) {
        CompilerAsserts.neverPartOfCompilation();
        propertyListAllocCount.inc();
        List ownProperties = shape.getPropertyList();
        sortProperties(ownProperties);
        return ownProperties.toArray(EMPTY_PROPERTY_ARRAY);
    }

    private static TruffleString[] createEnumerablePropertyNamesArray(Shape shape) {
        CompilerAsserts.neverPartOfCompilation();
        enumerablePropertyListAllocCount.inc();
        List ownProperties = new ArrayList<>();
        shape.getPropertyList().forEach(property -> {
            if (JSProperty.isEnumerable(property) && property.getKey() instanceof TruffleString propertyName) {
                ownProperties.add(propertyName);
            }
        });
        sortPropertyKeys(ownProperties);
        return ownProperties.toArray(EMPTY_STRING_ARRAY);
    }

    private static void sortProperties(List ownProperties) {
        CompilerAsserts.neverPartOfCompilation();
        ownProperties.sort((o1, o2) -> JSRuntime.comparePropertyKeys(o1.getKey(), o2.getKey()));
    }

    private static void sortPropertyKeys(List ownProperties) {
        CompilerAsserts.neverPartOfCompilation();
        ownProperties.sort(JSRuntime::comparePropertyKeys);
    }

    private static JSShapeData getShapeData(Shape shape) {
        CompilerAsserts.neverPartOfCompilation();
        JSContext context = JSShape.getJSContext(shape);
        Map map = context.getShapeDataMap();
        JSShapeData shapeData = map.get(shape);
        if (shapeData == null) {
            shapeData = new JSShapeData();
            JSShapeData previous = map.putIfAbsent(shape, shapeData);
            if (previous != null) {
                shapeData = previous;
            }
        }
        return shapeData;
    }

    @TruffleBoundary
    private static Property[] getPropertiesArray(Shape shape) {
        assert shape.getPropertyCount() != 0;
        return getPropertiesArray(getShapeData(shape), shape);
    }

    private static Property[] getPropertiesArray(JSShapeData shapeData, Shape shape) {
        Property[] propertyArray = shapeData.propertyArray;
        if (propertyArray == null) {
            propertyArray = createPropertiesArray(shape);
            assert propertyArray.length == shape.getPropertyCount();
            shapeData.propertyArray = propertyArray;
        }
        return propertyArray;
    }

    private static int getSymbolsStart(JSShapeData shapeData, Property[] propertyArray) {
        int symbolsStart = shapeData.symbolsStartPos;
        if (symbolsStart == UNKNOWN) {
            shapeData.symbolsStartPos = symbolsStart = getSymbolsStart(propertyArray);
        }
        return symbolsStart;
    }

    private static int getSymbolsStart(Property[] propertyArray) {
        int pos = propertyArray.length;
        for (; pos > 0; pos--) {
            Property prev = propertyArray[pos - 1];
            if (!(prev.getKey() instanceof Symbol)) {
                break;
            }
        }
        return pos;
    }

    static UnmodifiableArrayList getProperties(Shape shape) {
        return asUnmodifiableList(shape.getPropertyCount() == 0 ? EMPTY_PROPERTY_ARRAY : getPropertiesArray(shape));
    }

    @TruffleBoundary
    private static TruffleString[] getEnumerablePropertyNamesArray(Shape shape) {
        assert shape.getPropertyCount() != 0;
        return getEnumerablePropertyNamesArray(getShapeData(shape), shape);
    }

    private static TruffleString[] getEnumerablePropertyNamesArray(JSShapeData shapeData, Shape shape) {
        TruffleString[] enumeratePropertyNames = shapeData.enumerablePropertyNames;
        if (enumeratePropertyNames == null) {
            enumeratePropertyNames = createEnumerablePropertyNamesArray(shape);
            shapeData.enumerablePropertyNames = enumeratePropertyNames;
        }
        return enumeratePropertyNames;
    }

    static UnmodifiableArrayList getEnumerablePropertyNames(Shape shape) {
        return asUnmodifiableList(shape.getPropertyCount() == 0 ? EMPTY_STRING_ARRAY : getEnumerablePropertyNamesArray(shape));
    }

    @TruffleBoundary
    private static Property[] getPropertiesArrayIfHasEnumerablePropertyNames(Shape shape) {
        assert shape.getPropertyCount() != 0;
        JSShapeData shapeData = getShapeData(shape);
        if (getEnumerablePropertyNamesArray(shapeData, shape).length == 0) {
            return EMPTY_PROPERTY_ARRAY;
        } else {
            return getPropertiesArray(shapeData, shape);
        }
    }

    static UnmodifiableArrayList getPropertiesIfHasEnumerablePropertyNames(Shape shape) {
        return asUnmodifiableList(shape.getPropertyCount() == 0 ? EMPTY_PROPERTY_ARRAY : getPropertiesArrayIfHasEnumerablePropertyNames(shape));
    }

    static  UnmodifiablePropertyKeyList getPropertyKeyList(Shape shape, boolean strings, boolean symbols) {
        CompilerAsserts.neverPartOfCompilation();
        Property[] propertyArray;
        int start;
        int end;
        if (shape.getPropertyCount() == 0) {
            propertyArray = EMPTY_PROPERTY_ARRAY;
            start = 0;
            end = 0;
        } else {
            JSShapeData shapeData = getShapeData(shape);
            propertyArray = getPropertiesArray(shapeData, shape);
            start = 0;
            end = propertyArray.length;
            if (!strings || !symbols) {
                int symbolsStart = getSymbolsStart(shapeData, propertyArray);
                if (!strings) {
                    start = symbolsStart;
                }
                if (!symbols) {
                    end = symbolsStart;
                }
            }
        }
        return UnmodifiablePropertyKeyList.create(propertyArray, start, end);
    }

    private static  UnmodifiableArrayList asUnmodifiableList(T[] array) {
        return new UnmodifiableArrayList<>(array);
    }

    private static final DebugCounter enumerablePropertyListAllocCount = DebugCounter.create("Enumerable property lists allocated");
    private static final DebugCounter propertyListAllocCount = DebugCounter.create("Property lists allocated");
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy