From be7c9a7a29e490d2eb3318697bb882f2782d7f87 Mon Sep 17 00:00:00 2001 From: Thomas Schrott Date: Wed, 28 Aug 2024 09:40:59 +0200 Subject: [PATCH] Refactor the IsolateArgumentParser --- .../svm/core/IsolateArgumentAccess.java | 74 ++++ .../svm/core/IsolateArgumentParser.java | 361 ++++++++++++------ .../com/oracle/svm/core/IsolateArguments.java | 80 ++++ .../src/com/oracle/svm/core/Isolates.java | 5 +- .../graal/snippets/CEntryPointSnippets.java | 39 +- .../oracle/svm/core/heap/PhysicalMemory.java | 2 +- .../svm/core/heap/ReferenceHandler.java | 2 +- .../oracle/svm/core/jdk/RuntimeSupport.java | 2 +- .../oracle/svm/core/jvmstat/PerfManager.java | 7 +- .../svm/core/os/CommittedMemoryProvider.java | 7 + 10 files changed, 446 insertions(+), 133 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentAccess.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArguments.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentAccess.java new file mode 100644 index 000000000000..5a1099b4d4a3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentAccess.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core; + +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.word.WordFactory; + +public class IsolateArgumentAccess { + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static long readLong(IsolateArguments arguments, int index) { + return arguments.getParsedArgs().read(index); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void writeLong(IsolateArguments arguments, int index, long value) { + arguments.getParsedArgs().write(index, value); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static int readInt(IsolateArguments arguments, int index) { + return (int) readLong(arguments, index); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void writeInt(IsolateArguments arguments, int index, int value) { + writeLong(arguments, index, value); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static boolean readBoolean(IsolateArguments arguments, int index) { + return readLong(arguments, index) == 1L; + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void writeBoolean(IsolateArguments arguments, int index, boolean value) { + writeLong(arguments, index, value ? 1L : 0L); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static CCharPointer readCCharPointer(IsolateArguments arguments, int index) { + return WordFactory.pointer(readLong(arguments, index)); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void writeCCharPointer(IsolateArguments arguments, int index, CCharPointer value) { + writeLong(arguments, index, value.rawValue()); + } + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentParser.java index 75760ec248ee..33f3eb698e31 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentParser.java @@ -24,10 +24,18 @@ */ package com.oracle.svm.core; +import static com.oracle.svm.core.IsolateArgumentAccess.readCCharPointer; +import static com.oracle.svm.core.IsolateArgumentAccess.readLong; +import static com.oracle.svm.core.IsolateArgumentAccess.writeBoolean; +import static com.oracle.svm.core.IsolateArgumentAccess.writeCCharPointer; +import static com.oracle.svm.core.IsolateArgumentAccess.writeLong; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; @@ -35,6 +43,7 @@ import org.graalvm.nativeimage.c.type.CCharPointerPointer; import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.CLongPointer; +import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -44,6 +53,7 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.graal.RuntimeCompilation; import com.oracle.svm.core.headers.LibC; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.util.VMError; @@ -52,42 +62,57 @@ /** * Parses a small subset of the runtime arguments before the image heap is mapped and before the * isolate is fully started. + * + * If options are specified in {@link CEntryPointCreateIsolateParameters} and {@code argv} the value + * stored in {@code argv} is used. */ @AutomaticallyRegisteredImageSingleton public class IsolateArgumentParser { private static final RuntimeOptionKey[] OPTIONS = {SubstrateGCOptions.MinHeapSize, SubstrateGCOptions.MaxHeapSize, SubstrateGCOptions.MaxNewSize, SubstrateGCOptions.ReservedAddressSpaceSize, SubstrateOptions.ConcealedOptions.AutomaticReferenceHandling, SubstrateOptions.ConcealedOptions.UsePerfData, SubstrateOptions.MaxRAM}; - private static final int OPTION_COUNT = OPTIONS.length; private static final CGlobalData OPTION_NAMES = CGlobalDataFactory.createBytes(IsolateArgumentParser::createOptionNames); private static final CGlobalData OPTION_NAME_POSITIONS = CGlobalDataFactory.createBytes(IsolateArgumentParser::createOptionNamePosition); private static final CGlobalData OPTION_TYPES = CGlobalDataFactory.createBytes(IsolateArgumentParser::createOptionTypes); - private static final CGlobalData HOSTED_VALUES = CGlobalDataFactory.createBytes(IsolateArgumentParser::createHostedValues); - private static final long[] PARSED_OPTION_VALUES = new long[OPTION_COUNT]; + protected static final CGlobalData DEFAULT_VALUES = CGlobalDataFactory.createBytes(IsolateArgumentParser::createDefaultValues); + + /** + * All values (regardless of their type) are stored as 8 byte values. See + * {@link IsolateArguments#setParsedArgs(CLongPointer)} for more information. + * + * For directly initializing this array, no method depending on + * {@link IsolateArgumentParser#singleton()} may be used. + */ + private final long[] parsedOptionValues = new long[getOptions0().length]; private static final long K = 1024; private static final long M = K * K; private static final long G = K * M; private static final long T = K * G; - private static boolean isCompilationIsolate; + private boolean isCompilationIsolate; @Platforms(Platform.HOSTED_ONLY.class) public IsolateArgumentParser() { } + @Fold + public static IsolateArgumentParser singleton() { + return ImageSingletons.lookup(IsolateArgumentParser.class); + } + @Platforms(Platform.HOSTED_ONLY.class) private static byte[] createOptionNames() { - StringBuilder options = new StringBuilder(); - for (int i = 0; i < OPTION_COUNT; i++) { - options.append(OPTIONS[i].getName()); - options.append("\0"); + StringBuilder optionNames = new StringBuilder(); + for (int i = 0; i < getOptionCount(); i++) { + optionNames.append(getOptions()[i].getName()); + optionNames.append("\0"); } - return options.toString().getBytes(StandardCharsets.ISO_8859_1); + return optionNames.toString().getBytes(StandardCharsets.ISO_8859_1); } @Platforms(Platform.HOSTED_ONLY.class) private static byte[] createOptionNamePosition() { - byte[] result = new byte[Integer.BYTES * OPTION_COUNT]; + byte[] result = new byte[Integer.BYTES * getOptionCount()]; ByteBuffer buffer = ByteBuffer.wrap(result).order(ByteOrder.nativeOrder()); buffer.putInt(0); @@ -102,30 +127,32 @@ private static byte[] createOptionNamePosition() { @Platforms(Platform.HOSTED_ONLY.class) private static byte[] createOptionTypes() { - byte[] result = new byte[Byte.BYTES * OPTION_COUNT]; + byte[] result = new byte[Byte.BYTES * getOptionCount()]; ByteBuffer buffer = ByteBuffer.wrap(result).order(ByteOrder.nativeOrder()); - for (int i = 0; i < OPTION_COUNT; i++) { - Class optionValueType = OPTIONS[i].getDescriptor().getOptionValueType(); + for (int i = 0; i < getOptionCount(); i++) { + Class optionValueType = getOptions()[i].getDescriptor().getOptionValueType(); buffer.put(OptionValueType.fromClass(optionValueType)); } return result; } @Platforms(Platform.HOSTED_ONLY.class) - private static byte[] createHostedValues() { - byte[] result = new byte[Long.BYTES * OPTION_COUNT]; + private static byte[] createDefaultValues() { + byte[] result = new byte[Long.BYTES * getOptionCount()]; ByteBuffer buffer = ByteBuffer.wrap(result).order(ByteOrder.nativeOrder()); - for (int i = 0; i < OPTION_COUNT; i++) { - long value = toLong(OPTIONS[i].getHostedValue()); + for (int i = 0; i < getOptionCount(); i++) { + long value = toLong(getOptions()[i].getHostedValue(), getOptions()[i].getDescriptor().getOptionValueType()); buffer.putLong(value); } return result; } @Platforms(Platform.HOSTED_ONLY.class) - private static long toLong(Object value) { - if (value instanceof Boolean) { - return ((Boolean) value) ? 1 : 0; + private static long toLong(Object value, Class clazz) { + if (value == null && String.class.equals(clazz)) { + return 0L; + } else if (value instanceof Boolean) { + return ((Boolean) value) ? 1L : 0L; } else if (value instanceof Integer) { return (Integer) value; } else if (value instanceof Long) { @@ -135,41 +162,107 @@ private static long toLong(Object value) { } } + @Fold + protected static RuntimeOptionKey[] getOptions() { + return singleton().getOptions0(); + } + + @Fold + protected RuntimeOptionKey[] getOptions0() { + return OPTIONS; + } + + @Fold + protected static int getOptionCount() { + return getOptions().length; + } + @Uninterruptible(reason = "Still being initialized.") - public static void parse(CEntryPointCreateIsolateParameters parameters, CLongPointer parsedArgs) { - initialize(parsedArgs); - if (!LibC.isSupported() || !shouldParseArguments(parameters)) { + public void parse(CEntryPointCreateIsolateParameters parameters, IsolateArguments arguments) { + initialize(arguments, parameters); + if (!LibC.isSupported() || !shouldParseArguments(arguments)) { // Without LibC support, argument parsing is disabled. So, we just use the build-time // values of the runtime options. return; } - int argc = 0; - CCharPointerPointer argv = WordFactory.nullPointer(); - if (parameters.isNonNull() && parameters.version() >= 3 && parameters.getArgv().isNonNull()) { - argc = parameters.getArgc(); - argv = parameters.getArgv(); - } - - CLongPointer numericValue = StackValue.get(Long.BYTES); + CLongPointer value = StackValue.get(Long.BYTES); // Ignore the first argument as it represents the executable file name. - for (int i = 1; i < argc; i++) { - CCharPointer arg = argv.read(i); + for (int i = 1; i < arguments.getArgc(); i++) { + CCharPointer arg = arguments.getArgv().read(i); if (arg.isNonNull()) { CCharPointer tail = matchPrefix(arg); if (tail.isNonNull()) { CCharPointer xOptionTail = matchXOption(tail); if (xOptionTail.isNonNull()) { - parseXOption(parsedArgs, numericValue, xOptionTail); + parseXOption(arguments, value, xOptionTail); } else { CCharPointer xxOptionTail = matchXXOption(tail); if (xxOptionTail.isNonNull()) { - parseXXOption(parsedArgs, numericValue, xxOptionTail); + parseXXOption(arguments, value, xxOptionTail); } } } } } + + copyStringArguments(arguments); + + // Temporarily needed. + overrideParameters(parameters, arguments); + } + + @Uninterruptible(reason = "Tear-down in progress.") + public boolean tearDown(IsolateArguments arguments) { + for (int i = 0; i < getOptionCount(); i++) { + if (OPTION_TYPES.get().read(i) == OptionValueType.C_CHAR_POINTER) { + UntrackedNullableNativeMemory.free(readCCharPointer(arguments, i)); + writeCCharPointer(arguments, i, WordFactory.nullPointer()); + } + } + return true; + } + + @Uninterruptible(reason = "Tear-down in progress.") + public boolean tearDown() { + for (int i = 0; i < getOptionCount(); i++) { + if (OPTION_TYPES.get().read(i) == OptionValueType.C_CHAR_POINTER) { + UntrackedNullableNativeMemory.free(WordFactory.pointer(parsedOptionValues[i])); + parsedOptionValues[i] = 0; + } + } + return true; + } + + public void copyToRuntimeOptions() { + int index = getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize); + long value = getLongOptionValue(index); + if (DEFAULT_VALUES.get().read(index) != value) { + SubstrateGCOptions.ReservedAddressSpaceSize.update(value); + } + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static void copyStringArguments(IsolateArguments arguments) { + for (int i = 0; i < getOptionCount(); i++) { + if (OPTION_TYPES.get().read(i) == OptionValueType.C_CHAR_POINTER) { + CCharPointer string = readCCharPointer(arguments, i); + + if (string.isNonNull()) { + CCharPointer copy = LibC.strdup(string); + VMError.guarantee(copy.isNonNull(), "Copying of string argument failed."); + writeCCharPointer(arguments, i, copy); + } else { + writeCCharPointer(arguments, i, WordFactory.nullPointer()); + } + } + } + } + + // Temporary method. + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + protected void overrideParameters(CEntryPointCreateIsolateParameters parameters, IsolateArguments arguments) { + parameters.setReservedSpaceSize(WordFactory.unsigned(readLong(arguments, getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize)))); } /** @@ -177,32 +270,32 @@ public static void parse(CEntryPointCreateIsolateParameters parameters, CLongPoi * {@link com.oracle.svm.core.option.RuntimeOptionParser#parseAndConsumeAllOptions}. */ @Uninterruptible(reason = "Thread state not yet set up.") - public static boolean shouldParseArguments(CEntryPointCreateIsolateParameters parameters) { + public static boolean shouldParseArguments(IsolateArguments arguments) { if (SubstrateOptions.ParseRuntimeOptions.getValue()) { return true; } else if (RuntimeCompilation.isEnabled() && SubstrateOptions.supportCompileInIsolates()) { - return isCompilationIsolate(parameters); + return isCompilationIsolate(arguments); } return false; } @Uninterruptible(reason = "Thread state not yet set up.") - private static boolean isCompilationIsolate(CEntryPointCreateIsolateParameters parameters) { - return parameters.version() >= 5 && parameters.getIsCompilationIsolate(); + private static boolean isCompilationIsolate(IsolateArguments arguments) { + return arguments.getIsCompilationIsolate(); } @Uninterruptible(reason = "Thread state not yet set up.") - public static void persistOptions(CEntryPointCreateIsolateParameters parameters, CLongPointer parsedArgs) { - isCompilationIsolate = isCompilationIsolate(parameters); + public void persistOptions(IsolateArguments arguments) { + isCompilationIsolate = isCompilationIsolate(arguments); - for (int i = 0; i < OPTION_COUNT; i++) { - PARSED_OPTION_VALUES[i] = parsedArgs.read(i); + for (int i = 0; i < getOptionCount(); i++) { + parsedOptionValues[i] = readLong(arguments, i); } } - public static void verifyOptionValues() { - for (int i = 0; i < OPTION_COUNT; i++) { - RuntimeOptionKey option = OPTIONS[i]; + public void verifyOptionValues() { + for (int i = 0; i < getOptionCount(); i++) { + RuntimeOptionKey option = getOptions()[i]; if (shouldValidate(option)) { validate(option, getOptionValue(i)); } @@ -217,36 +310,46 @@ private static boolean shouldValidate(RuntimeOptionKey option) { return true; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public static boolean isCompilationIsolate() { - return isCompilationIsolate; + return singleton().isCompilationIsolate; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static boolean getBooleanOptionValue(int index) { - return PARSED_OPTION_VALUES[index] == 1; + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public boolean getBooleanOptionValue(int index) { + return parsedOptionValues[index] == 1L; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static int getIntOptionValue(int index) { - return (int) PARSED_OPTION_VALUES[index]; + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public int getIntOptionValue(int index) { + return (int) parsedOptionValues[index]; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static long getLongOptionValue(int index) { - return PARSED_OPTION_VALUES[index]; + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public long getLongOptionValue(int index) { + return parsedOptionValues[index]; } - private static Object getOptionValue(int index) { - Class optionValueType = OPTIONS[index].getDescriptor().getOptionValueType(); - long value = PARSED_OPTION_VALUES[index]; + protected CCharPointer getCCharPointerOptionValue(int index) { + return WordFactory.pointer(parsedOptionValues[index]); + } + + protected Object getOptionValue(int index) { + Class optionValueType = getOptions()[index].getDescriptor().getOptionValueType(); + long value = parsedOptionValues[index]; if (optionValueType == Boolean.class) { - assert value == 0 || value == 1 : value; - return value == 1; + assert value == 0L || value == 1L : value; + return value == 1L; } else if (optionValueType == Integer.class) { return (int) value; } else if (optionValueType == Long.class) { return value; + } else if (optionValueType == String.class) { + if (value == 0L) { + return null; + } + + return CTypeConversion.toJavaString(WordFactory.pointer(value)); } else { throw VMError.shouldNotReachHere("Option value has unexpected type: " + optionValueType); } @@ -254,20 +357,42 @@ private static Object getOptionValue(int index) { private static void validate(RuntimeOptionKey option, Object oldValue) { Object newValue = option.getValue(); + if (oldValue == newValue) { + return; + } + if (newValue == null || !newValue.equals(oldValue)) { throw new IllegalArgumentException( "The option '" + option.getName() + "' can't be changed after isolate creation. Old value: " + oldValue + ", new value: " + newValue); } } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static void initialize(CLongPointer parsedArgs) { - for (int i = 0; i < OPTION_COUNT; i++) { - parsedArgs.write(i, HOSTED_VALUES.get().read(i)); + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + protected void initialize(IsolateArguments arguments, CEntryPointCreateIsolateParameters parameters) { + for (int i = 0; i < getOptionCount(); i++) { + writeLong(arguments, i, DEFAULT_VALUES.get().read(i)); } + + if (parameters.isNonNull() && parameters.version() >= 3) { + arguments.setArgc(parameters.getArgc()); + arguments.setArgv(parameters.getArgv()); + arguments.setProtectionKey(parameters.protectionKey()); + } else { + arguments.setArgc(0); + arguments.setArgv(WordFactory.nullPointer()); + arguments.setProtectionKey(0); + } + + if (parameters.isNonNull() && parameters.version() >= 5) { + arguments.setIsCompilationIsolate(parameters.getIsCompilationIsolate()); + } else { + arguments.setIsCompilationIsolate(false); + } + + writeLong(arguments, getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize), parameters.reservedSpaceSize().rawValue()); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static CCharPointer matchPrefix(CCharPointer arg) { if (arg.read(0) == '-') { return arg.addressOf(1); @@ -275,7 +400,7 @@ private static CCharPointer matchPrefix(CCharPointer arg) { return WordFactory.nullPointer(); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static CCharPointer matchXOption(CCharPointer arg) { if (arg.read(0) == 'X' && arg.read(1) == 'm') { return arg.addressOf(2); @@ -283,7 +408,7 @@ private static CCharPointer matchXOption(CCharPointer arg) { return WordFactory.nullPointer(); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static CCharPointer matchXXOption(CCharPointer arg) { if (arg.read(0) == 'X' && arg.read(1) == 'X' && arg.read(2) == ':') { return arg.addressOf(3); @@ -291,53 +416,53 @@ private static CCharPointer matchXXOption(CCharPointer arg) { return WordFactory.nullPointer(); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static void parseXOption(CLongPointer parsedArgs, CLongPointer numericValue, CCharPointer tail) { + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static void parseXOption(IsolateArguments arguments, CLongPointer value, CCharPointer tail) { byte kind = tail.read(); - if (kind == 's' && parseNumericXOption(tail.addressOf(1), numericValue)) { - parsedArgs.write(getOptionIndex(SubstrateGCOptions.MinHeapSize), numericValue.read()); - } else if (kind == 'x' && parseNumericXOption(tail.addressOf(1), numericValue)) { - parsedArgs.write(getOptionIndex(SubstrateGCOptions.MaxHeapSize), numericValue.read()); - } else if (kind == 'n' && parseNumericXOption(tail.addressOf(1), numericValue)) { - parsedArgs.write(getOptionIndex(SubstrateGCOptions.MaxNewSize), numericValue.read()); + if (kind == 's' && parseNumericXOption(tail.addressOf(1), value)) { + writeLong(arguments, getOptionIndex(SubstrateGCOptions.MinHeapSize), value.read()); + } else if (kind == 'x' && parseNumericXOption(tail.addressOf(1), value)) { + writeLong(arguments, getOptionIndex(SubstrateGCOptions.MaxHeapSize), value.read()); + } else if (kind == 'n' && parseNumericXOption(tail.addressOf(1), value)) { + writeLong(arguments, getOptionIndex(SubstrateGCOptions.MaxNewSize), value.read()); } } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static void parseXXOption(CLongPointer parsedArgs, CLongPointer numericValue, CCharPointer tail) { + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static void parseXXOption(IsolateArguments arguments, CLongPointer value, CCharPointer tail) { byte firstChar = tail.read(); if (firstChar == '+' || firstChar == '-') { - boolean value = firstChar == '+'; - for (int i = 0; i < OPTION_COUNT; i++) { + boolean booleanValue = firstChar == '+'; + for (int i = 0; i < getOptionCount(); i++) { int pos = OPTION_NAME_POSITIONS.get().read(i); CCharPointer optionName = OPTION_NAMES.get().addressOf(pos); - if (OPTION_TYPES.get().read(i) == OptionValueType.Boolean && matches(tail.addressOf(1), optionName)) { - parsedArgs.write(i, value ? 1 : 0); + if (OPTION_TYPES.get().read(i) == OptionValueType.BOOLEAN && matches(tail.addressOf(1), optionName)) { + writeBoolean(arguments, i, booleanValue); break; } } } else { - for (int i = 0; i < OPTION_COUNT; i++) { + for (int i = 0; i < getOptionCount(); i++) { int pos = OPTION_NAME_POSITIONS.get().read(i); CCharPointer optionName = OPTION_NAMES.get().addressOf(pos); - if (OptionValueType.isNumeric(OPTION_TYPES.get().read(i)) && parseNumericXXOption(tail, optionName, numericValue)) { - parsedArgs.write(i, numericValue.read()); - break; + CCharPointer valueStart = startsWith(tail, optionName); + + if (valueStart.isNonNull() && valueStart.read() == '=') { + if (OptionValueType.isNumeric(OPTION_TYPES.get().read(i))) { + parseNumericXOption(valueStart.addressOf(1), value); + writeLong(arguments, i, value.read()); + break; + } else if (OPTION_TYPES.get().read(i) == OptionValueType.C_CHAR_POINTER) { + writeCCharPointer(arguments, i, valueStart.addressOf(1)); + break; + } } - } - } - } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static boolean parseNumericXXOption(CCharPointer input, CCharPointer optionName, CLongPointer result) { - CCharPointer tail = startsWith(input, optionName); - if (tail.isNull() || tail.read() != '=') { - return false; + } } - return parseNumericXOption(tail.addressOf(1), result); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static boolean parseNumericXOption(CCharPointer string, CLongPointer result) { CCharPointer pos = string; @@ -357,7 +482,7 @@ private static boolean parseNumericXOption(CCharPointer string, CLongPointer res return true; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static boolean atojulong(CCharPointer s, CLongPointer result) { /* First char must be a digit. Don't allow negative numbers or leading spaces. */ if (LibC.isdigit(s.read()) == 0) { @@ -401,18 +526,18 @@ private static boolean atojulong(CCharPointer s, CLongPointer result) { return true; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static boolean checkForOverflow(UnsignedWord value, UnsignedWord n, long modifier) { return value.unsignedDivide(WordFactory.unsigned(modifier)) != n; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static boolean matches(CCharPointer input, CCharPointer expected) { CCharPointer tail = startsWith(input, expected); return tail.isNonNull() && tail.read() == 0; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static CCharPointer startsWith(CCharPointer input, CCharPointer prefix) { int i = 0; while (prefix.read(i) != 0 && input.read(i) == prefix.read(i)) { @@ -427,8 +552,13 @@ private static CCharPointer startsWith(CCharPointer input, CCharPointer prefix) @Fold public static int getOptionIndex(RuntimeOptionKey key) { - for (int i = 0; i < OPTIONS.length; i++) { - if (OPTIONS[i] == key) { + return singleton().getOptionIndex0(key); + } + + @Fold + protected int getOptionIndex0(RuntimeOptionKey key) { + for (int i = 0; i < getOptionCount(); i++) { + if (getOptions()[i] == key) { return i; } } @@ -437,30 +567,33 @@ public static int getOptionIndex(RuntimeOptionKey key) { } @Fold - public static int getStructSize() { - return Long.BYTES * OPTION_COUNT; + public int getParsedArgsSize() { + return Long.BYTES * getOptionCount(); } - private static class OptionValueType { - public static byte Boolean = 1; - public static byte Integer = 2; - public static byte Long = 3; + protected static class OptionValueType { + public static final byte BOOLEAN = 1; + public static final byte INTEGER = 2; + public static final byte LONG = 3; + public static final byte C_CHAR_POINTER = 4; public static byte fromClass(Class c) { if (c == Boolean.class) { - return Boolean; + return BOOLEAN; } else if (c == Integer.class) { - return Integer; + return INTEGER; } else if (c == Long.class) { - return Long; + return LONG; + } else if (c == String.class) { + return C_CHAR_POINTER; } else { throw VMError.shouldNotReachHere("Option value has unexpected type: " + c); } } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public static boolean isNumeric(byte optionValueType) { - return optionValueType == Integer || optionValueType == Long; + return optionValueType == INTEGER || optionValueType == LONG; } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArguments.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArguments.java new file mode 100644 index 000000000000..0e16ef1730a2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArguments.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core; + +import org.graalvm.nativeimage.c.struct.RawField; +import org.graalvm.nativeimage.c.struct.RawStructure; +import org.graalvm.nativeimage.c.type.CCharPointerPointer; +import org.graalvm.nativeimage.c.type.CLongPointer; +import org.graalvm.word.PointerBase; + +import com.oracle.svm.core.c.function.CEntryPointCreateIsolateParameters; + +@RawStructure +public interface IsolateArguments extends PointerBase { + + @RawField + void setArgc(int argc); + + @RawField + int getArgc(); + + @RawField + void setArgv(CCharPointerPointer argv); + + @RawField + CCharPointerPointer getArgv(); + + /** + * All argument values are stored as 8 byte values, regardless of their actual type. Therefore + * {@code CLongPointer} and {@code long} are used if arbitrary option types are handled. + */ + @RawField + void setParsedArgs(CLongPointer ptr); + + @RawField + CLongPointer getParsedArgs(); + + @RawField + void setProtectionKey(int pkey); + + // Temporary unused. + @RawField + int getProtectionKey(); + + @RawField + void setIsCompilationIsolate(boolean value); + + @RawField + boolean getIsCompilationIsolate(); + + // Temporary field. + @RawField + void setParameters(CEntryPointCreateIsolateParameters parameters); + + @RawField + CEntryPointCreateIsolateParameters getParameters(); + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java index e7c9798c0cc9..5939e00f6c42 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java @@ -36,7 +36,6 @@ import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; -import com.oracle.svm.core.c.function.CEntryPointCreateIsolateParameters; import com.oracle.svm.core.c.function.CEntryPointErrors; import com.oracle.svm.core.os.CommittedMemoryProvider; import com.oracle.svm.core.util.VMError; @@ -135,7 +134,7 @@ public static int checkIsolate(Isolate isolate) { } @Uninterruptible(reason = "Thread state not yet set up.") - public static int create(WordPointer isolatePointer, CEntryPointCreateIsolateParameters parameters) { + public static int create(WordPointer isolatePointer, IsolateArguments arguments) { if (!SubstrateOptions.SpawnIsolates.getValue()) { if (!SINGLE_ISOLATE_ALREADY_CREATED.get().logicCompareAndSwapWord(0, WordFactory.zero(), WordFactory.signed(1), NamedLocationIdentity.OFF_HEAP_LOCATION)) { return CEntryPointErrors.SINGLE_ISOLATE_ALREADY_CREATED; @@ -143,7 +142,7 @@ public static int create(WordPointer isolatePointer, CEntryPointCreateIsolatePar } WordPointer heapBasePointer = StackValue.get(WordPointer.class); - int result = CommittedMemoryProvider.get().initialize(heapBasePointer, parameters); + int result = CommittedMemoryProvider.get().initialize(heapBasePointer, arguments); if (result != CEntryPointErrors.NO_ERROR) { return result; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java index 7a8e3873251c..89db07381e90 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java @@ -37,26 +37,29 @@ import org.graalvm.nativeimage.Isolate; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CLongPointer; import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.word.LocationIdentity; +import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.CPUFeatureAccess; import com.oracle.svm.core.IsolateArgumentParser; +import com.oracle.svm.core.IsolateArguments; import com.oracle.svm.core.IsolateListenerSupport; import com.oracle.svm.core.Isolates; import com.oracle.svm.core.JavaMainWrapper.JavaMainSupport; import com.oracle.svm.core.NeverInline; import com.oracle.svm.core.RuntimeAssertionsSupport; import com.oracle.svm.core.SubstrateDiagnostics; -import com.oracle.svm.core.SubstrateGCOptions; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.UnmanagedMemoryUtil; import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; import com.oracle.svm.core.c.NonmovableArrays; @@ -207,6 +210,12 @@ public static int createIsolateSnippet(CEntryPointCreateIsolateParameters parame return runtimeCallInitializeIsolate(INITIALIZE_ISOLATE, parameters); } + /** + * After parsing the isolate arguments in + * {@link IsolateArgumentParser#parse(CEntryPointCreateIsolateParameters, IsolateArguments)} the + * {@code providedParameters} should no longer be used. Instead {@link IsolateArguments} + * contains the correct values. + */ @Uninterruptible(reason = "Thread state not yet set up.") @SubstrateForeignCallTarget(stubCallingConvention = false) private static int createIsolate(CEntryPointCreateIsolateParameters providedParameters) { @@ -227,28 +236,35 @@ private static int createIsolate(CEntryPointCreateIsolateParameters providedPara parameters.setReservedSpaceSize(WordFactory.zero()); parameters.setVersion(1); } - CLongPointer parsedArgs = StackValue.get(IsolateArgumentParser.getStructSize()); - IsolateArgumentParser.parse(parameters, parsedArgs); - if (parameters.reservedSpaceSize().equal(0)) { - parameters.setReservedSpaceSize(WordFactory.unsigned(parsedArgs.read(IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize)))); - } + + IsolateArguments arguments = StackValue.get(IsolateArguments.class); + UnmanagedMemoryUtil.fill((Pointer) arguments, SizeOf.unsigned(IsolateArguments.class), (byte) 0); + CLongPointer parsedArgs = StackValue.get(IsolateArgumentParser.singleton().getParsedArgsSize()); + arguments.setParsedArgs(parsedArgs); + + // Temporarily needed. + arguments.setParameters(parameters); + + IsolateArgumentParser.singleton().parse(parameters, arguments); + Container.initialize(); WordPointer isolatePtr = StackValue.get(WordPointer.class); - int error = Isolates.create(isolatePtr, parameters); + int error = Isolates.create(isolatePtr, arguments); if (error != CEntryPointErrors.NO_ERROR) { + IsolateArgumentParser.singleton().tearDown(arguments); return error; } Isolate isolate = isolatePtr.read(); setHeapBase(Isolates.getHeapBase(isolate)); - return createIsolate0(isolate, parameters, parsedArgs); + return createIsolate0(isolate, arguments); } @Uninterruptible(reason = "Thread state not yet set up.") @NeverInline(value = "Ensure this code cannot rise above where heap base is set.") - private static int createIsolate0(Isolate isolate, CEntryPointCreateIsolateParameters parameters, CLongPointer parsedArgs) { - IsolateArgumentParser.persistOptions(parameters, parsedArgs); + private static int createIsolate0(Isolate isolate, IsolateArguments arguments) { + IsolateArgumentParser.singleton().persistOptions(arguments); IsolateListenerSupport.singleton().afterCreateIsolate(isolate); CodeInfoTable.prepareImageCodeInfo(); @@ -358,6 +374,8 @@ private static int initializeIsolateInterruptibly1(CEntryPointCreateIsolateParam * that allocates a significant amount of memory. */ + IsolateArgumentParser.singleton().copyToRuntimeOptions(); + if (parameters.isNonNull() && parameters.version() >= 3 && parameters.getArgv().isNonNull()) { boolean exitWhenArgumentParsingFails = true; boolean ignoreUnrecognized = false; @@ -603,6 +621,7 @@ private static int tearDownIsolate() { CodeInfoTable.tearDown(); NonmovableArrays.tearDown(); Heap.getHeap().tearDown(); + IsolateArgumentParser.singleton().tearDown(); /* Tear down the heap address space, including the image heap. */ int code = CommittedMemoryProvider.get().tearDown(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java index 85daaba4f0d7..72758b6a3004 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java @@ -81,7 +81,7 @@ private static boolean isInitialized() { */ public static void initialize() { assert !isInitialized() : "Physical memory already initialized."; - long memoryLimit = IsolateArgumentParser.getLongOptionValue(IsolateArgumentParser.getOptionIndex(SubstrateOptions.MaxRAM)); + long memoryLimit = IsolateArgumentParser.singleton().getLongOptionValue(IsolateArgumentParser.getOptionIndex(SubstrateOptions.MaxRAM)); if (memoryLimit > 0) { cachedSize = WordFactory.unsigned(memoryLimit); } else if (Container.singleton().isContainerized()) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceHandler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceHandler.java index b6f53ad95e10..221808063870 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceHandler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceHandler.java @@ -36,7 +36,7 @@ public final class ReferenceHandler { public static boolean useDedicatedThread() { int automaticReferenceHandling = IsolateArgumentParser.getOptionIndex(SubstrateOptions.ConcealedOptions.AutomaticReferenceHandling); - return ReferenceHandlerThread.isSupported() && IsolateArgumentParser.getBooleanOptionValue(automaticReferenceHandling); + return ReferenceHandlerThread.isSupported() && IsolateArgumentParser.singleton().getBooleanOptionValue(automaticReferenceHandling); } public static boolean isExecutedManually() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeSupport.java index 5feb1e533147..bc95bf523cba 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeSupport.java @@ -94,7 +94,7 @@ public boolean isUninitialized() { public void initialize() { boolean shouldInitialize = initializationState.compareAndSet(InitializationState.Uninitialized, InitializationState.InProgress); if (shouldInitialize) { - IsolateArgumentParser.verifyOptionValues(); + IsolateArgumentParser.singleton().verifyOptionValues(); HeapSizeVerifier.verifyHeapOptions(); executeHooks(startupHooks); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfManager.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfManager.java index 101b7cff4215..380d1d1a61b7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfManager.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfManager.java @@ -30,8 +30,6 @@ import java.util.ArrayList; import org.graalvm.collections.EconomicMap; -import jdk.graal.compiler.options.Option; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -49,6 +47,9 @@ import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.word.Word; + /** * Used to create and manage performance data entries. */ @@ -110,7 +111,7 @@ public PerfStringVariable createStringVariable(String name, int lengthInBytes) { public static boolean usePerfData() { int optionIndex = IsolateArgumentParser.getOptionIndex(SubstrateOptions.ConcealedOptions.UsePerfData); - return VMInspectionOptions.hasJvmstatSupport() && IsolateArgumentParser.getBooleanOptionValue(optionIndex); + return VMInspectionOptions.hasJvmstatSupport() && IsolateArgumentParser.singleton().getBooleanOptionValue(optionIndex); } /** Returns a pointer into the image heap. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java index c665f03c32b6..a36381519cc3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java @@ -30,6 +30,7 @@ import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; +import com.oracle.svm.core.IsolateArguments; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.c.function.CEntryPointCreateIsolateParameters; @@ -54,6 +55,12 @@ static CommittedMemoryProvider get() { @Uninterruptible(reason = "Still being initialized.") int initialize(WordPointer heapBasePointer, CEntryPointCreateIsolateParameters parameters); + @Uninterruptible(reason = "Still being initialized.") + default int initialize(WordPointer heapBasePointer, IsolateArguments arguments) { + // The default implementation of this method is temporary. + return initialize(heapBasePointer, arguments.getParameters()); + } + /** * Tear down for the current isolate. This must be the last method of this interface * that is called in an isolate.