提交 5afdc343 编写于 作者: D duke

Merge

......@@ -432,3 +432,6 @@ b94be69cbb1d2943b886bf2d458745756df146e4 jdk-10+9
8d4ed1e06fe184c9cb08c5b708e7d6f5c066644f jdk-10+12
8f7227c6012b0051ea4e0bcee040c627bf699b88 jdk-9+175
d67a3f1f057f7e31e12f33ebe3667cb73d252268 jdk-10+13
1fd5901544acc50bb30fde9388c8e53cb7c449e4 jdk-10+14
84777531d994ef70163d35078ec9c4127f2eadb5 jdk-9+176
a4371edb589c60db01142e45c317adb9ccbcb083 jdk-9+177
......@@ -615,6 +615,8 @@ var getJibProfilesProfiles = function (input, common, data) {
}
var testOnlyProfilesPrebuilt = {
"run-test-prebuilt": {
target_os: input.build_os,
target_cpu: input.build_cpu,
src: "src.conf",
dependencies: [ "jtreg", "gnumake", "boot_jdk", testedProfile + ".jdk",
testedProfile + ".test", "src.full"
......@@ -635,13 +637,14 @@ var getJibProfilesProfiles = function (input, common, data) {
if (input.profile == "run-test-prebuilt") {
if (profiles[testedProfile] == null) {
error("testedProfile is not defined: " + testedProfile);
} else {
testOnlyProfilesPrebuilt["run-test-prebuilt"]["target_os"]
= profiles[testedProfile]["target_os"];
testOnlyProfilesPrebuilt["run-test-prebuilt"]["target_cpu"]
= profiles[testedProfile]["target_cpu"];
}
}
if (profiles[testedProfile] != null) {
testOnlyProfilesPrebuilt["run-test-prebuilt"]["target_os"]
= profiles[testedProfile]["target_os"];
testOnlyProfilesPrebuilt["run-test-prebuilt"]["target_cpu"]
= profiles[testedProfile]["target_cpu"];
}
profiles = concatObjects(profiles, testOnlyProfilesPrebuilt);
// On macosx add the devkit bin dir to the path in all the run-test profiles.
......
此差异已折叠。
此差异已折叠。
......@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Testing OpenJDK</title>
<style type="text/css">code{white-space: pre;}</style>
<link rel="stylesheet" href="../../jdk/make/data/docs-resources/specs/resources/jdk-default.css">
<link rel="stylesheet" href="../../jdk/make/data/docs-resources/resources/jdk-default.css">
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
......
......@@ -592,3 +592,6 @@ e64b1cb48d6e7703928a9d1da106fc27f8cb65fd jdk-9+173
070aa7a2eb14c4645f7eb31384cba0a2ba72a4b5 jdk-10+12
8f04d457168b9f1f4a1b2c37f49e0513ca9d33a7 jdk-9+175
a9da03357f190807591177fe9846d6e68ad64fc0 jdk-10+13
e920b4d008d914f3414bd4630b58837cf0b7f08d jdk-10+14
2ab74e5dbdc2b6a962c865500cafd23cf387dc60 jdk-9+176
1ca8f038fceb88c640badf9bd18905205bc63b43 jdk-9+177
......@@ -748,41 +748,3 @@ bool ArrayCopyNode::modifies(intptr_t offset_lo, intptr_t offset_hi, PhaseTransf
return false;
}
// We try to replace a load from the destination of an arraycopy with
// a load from the source so the arraycopy has a chance to be
// eliminated. It's only valid if the arraycopy doesn't change the
// element that would be loaded from the source array.
bool ArrayCopyNode::can_replace_dest_load_with_src_load(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase) const {
assert(_kind == ArrayCopy || _kind == CopyOf || _kind == CopyOfRange, "only for real array copies");
Node* src = in(Src);
Node* dest = in(Dest);
// Check whether, assuming source and destination are the same
// array, the arraycopy modifies the element from the source we
// would load.
if ((src != dest && in(SrcPos) == in(DestPos)) || !modifies(offset_lo, offset_hi, phase, false)) {
// if not the transformation is legal
return true;
}
AllocateNode* src_alloc = AllocateNode::Ideal_allocation(src, phase);
AllocateNode* dest_alloc = AllocateNode::Ideal_allocation(dest, phase);
// Check whether source and destination can be proved to be
// different arrays
const TypeOopPtr* t_src = phase->type(src)->isa_oopptr();
const TypeOopPtr* t_dest = phase->type(dest)->isa_oopptr();
if (t_src != NULL && t_dest != NULL &&
(t_src->is_known_instance() || t_dest->is_known_instance()) &&
t_src->instance_id() != t_dest->instance_id()) {
return true;
}
if (MemNode::detect_ptr_independence(src->uncast(), src_alloc, dest->uncast(), dest_alloc, phase)) {
return true;
}
return false;
}
......@@ -168,7 +168,6 @@ public:
static bool may_modify(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase, ArrayCopyNode*& ac);
bool modifies(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase, bool must_modify) const;
bool can_replace_dest_load_with_src_load(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase) const;
#ifndef PRODUCT
virtual void dump_spec(outputStream *st) const;
......
......@@ -5171,6 +5171,10 @@ bool LibraryCallKit::inline_arraycopy() {
Deoptimization::Action_make_not_entrant);
assert(stopped(), "Should be stopped");
}
const TypeKlassPtr* dest_klass_t = _gvn.type(dest_klass)->is_klassptr();
const Type *toop = TypeOopPtr::make_from_klass(dest_klass_t->klass());
src = _gvn.transform(new CheckCastPPNode(control(), src, toop));
}
arraycopy_move_allocation_here(alloc, dest, saved_jvms, saved_reexecute_sp, new_idx);
......
......@@ -885,7 +885,7 @@ static bool skip_through_membars(Compile::AliasType* atp, const TypeInstPtr* tp,
// Is the value loaded previously stored by an arraycopy? If so return
// a load node that reads from the source array so we may be able to
// optimize out the ArrayCopy node later.
Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseGVN* phase) const {
Node* ld_adr = in(MemNode::Address);
intptr_t ld_off = 0;
AllocateNode* ld_alloc = AllocateNode::Ideal_allocation(ld_adr, phase, ld_off);
......@@ -893,23 +893,27 @@ Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
if (ac != NULL) {
assert(ac->is_ArrayCopy(), "what kind of node can this be?");
Node* ld = clone();
Node* mem = ac->in(TypeFunc::Memory);
Node* ctl = ac->in(0);
Node* src = ac->in(ArrayCopyNode::Src);
if (!ac->as_ArrayCopy()->is_clonebasic() && !phase->type(src)->isa_aryptr()) {
return NULL;
}
LoadNode* ld = clone()->as_Load();
Node* addp = in(MemNode::Address)->clone();
if (ac->as_ArrayCopy()->is_clonebasic()) {
assert(ld_alloc != NULL, "need an alloc");
Node* addp = in(MemNode::Address)->clone();
assert(addp->is_AddP(), "address must be addp");
assert(addp->in(AddPNode::Base) == ac->in(ArrayCopyNode::Dest)->in(AddPNode::Base), "strange pattern");
assert(addp->in(AddPNode::Address) == ac->in(ArrayCopyNode::Dest)->in(AddPNode::Address), "strange pattern");
addp->set_req(AddPNode::Base, ac->in(ArrayCopyNode::Src)->in(AddPNode::Base));
addp->set_req(AddPNode::Address, ac->in(ArrayCopyNode::Src)->in(AddPNode::Address));
ld->set_req(MemNode::Address, phase->transform(addp));
if (in(0) != NULL) {
assert(ld_alloc->in(0) != NULL, "alloc must have control");
ld->set_req(0, ld_alloc->in(0));
}
addp->set_req(AddPNode::Base, src->in(AddPNode::Base));
addp->set_req(AddPNode::Address, src->in(AddPNode::Address));
} else {
Node* src = ac->in(ArrayCopyNode::Src);
Node* addp = in(MemNode::Address)->clone();
assert(ac->as_ArrayCopy()->is_arraycopy_validated() ||
ac->as_ArrayCopy()->is_copyof_validated() ||
ac->as_ArrayCopy()->is_copyofrange_validated(), "only supported cases");
assert(addp->in(AddPNode::Base) == addp->in(AddPNode::Address), "should be");
addp->set_req(AddPNode::Base, src);
addp->set_req(AddPNode::Address, src);
......@@ -927,21 +931,17 @@ Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
Node* offset = phase->transform(new AddXNode(addp->in(AddPNode::Offset), diff));
addp->set_req(AddPNode::Offset, offset);
ld->set_req(MemNode::Address, phase->transform(addp));
const TypeX *ld_offs_t = phase->type(offset)->isa_intptr_t();
if (!ac->as_ArrayCopy()->can_replace_dest_load_with_src_load(ld_offs_t->_lo, ld_offs_t->_hi, phase)) {
return NULL;
}
if (in(0) != NULL) {
assert(ac->in(0) != NULL, "alloc must have control");
ld->set_req(0, ac->in(0));
}
}
addp = phase->transform(addp);
#ifdef ASSERT
const TypePtr* adr_type = phase->type(addp)->is_ptr();
ld->_adr_type = adr_type;
#endif
ld->set_req(MemNode::Address, addp);
ld->set_req(0, ctl);
ld->set_req(MemNode::Memory, mem);
// load depends on the tests that validate the arraycopy
ld->as_Load()->_control_dependency = Pinned;
ld->_control_dependency = Pinned;
return ld;
}
return NULL;
......
......@@ -270,7 +270,7 @@ protected:
const Type* load_array_final_field(const TypeKlassPtr *tkls,
ciKlass* klass) const;
Node* can_see_arraycopy_value(Node* st, PhaseTransform* phase) const;
Node* can_see_arraycopy_value(Node* st, PhaseGVN* phase) const;
// depends_only_on_test is almost always true, and needs to be almost always
// true to enable key hoisting & commoning optimizations. However, for the
......
/*
* Copyright (c) 2017, Red Hat, Inc. 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.
*
* 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.
*/
/**
* @test
* @bug 8181742
* @summary Loads that bypass arraycopy ends up with wrong memory state
*
* @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions -XX:+StressGCM -XX:+StressLCM TestLoadBypassACWithWrongMem
*
*/
import java.util.Arrays;
public class TestLoadBypassACWithWrongMem {
static int test1(int[] src) {
int[] dst = new int[10];
System.arraycopy(src, 0, dst, 0, 10);
src[1] = 0x42;
// dst[1] is transformed to src[1], src[1] must use the
// correct memory state (not the store above).
return dst[1];
}
static int test2(int[] src) {
int[] dst = (int[])src.clone();
src[1] = 0x42;
// Same as above for clone
return dst[1];
}
static Object test5_src = null;
static int test3() {
int[] dst = new int[10];
System.arraycopy(test5_src, 0, dst, 0, 10);
((int[])test5_src)[1] = 0x42;
System.arraycopy(test5_src, 0, dst, 0, 10);
// dst[1] is transformed to test5_src[1]. test5_src is Object
// but test5_src[1] must be on the slice for int[] not
// Object+some offset.
return dst[1];
}
static public void main(String[] args) {
int[] src = new int[10];
for (int i = 0; i < 20000; i++) {
Arrays.fill(src, 0);
int res = test1(src);
if (res != 0) {
throw new RuntimeException("bad result: " + res + " != " + 0);
}
Arrays.fill(src, 0);
res = test2(src);
if (res != 0) {
throw new RuntimeException("bad result: " + res + " != " + 0);
}
Arrays.fill(src, 0);
test5_src = src;
res = test3();
if (res != 0x42) {
throw new RuntimeException("bad result: " + res + " != " + 0x42);
}
}
}
}
......@@ -108,8 +108,8 @@ public class TestAnonymousClassUnloading {
*/
static public void main(String[] args) throws Exception {
// (1) Load an anonymous version of this class using the corresponding Unsafe method
URL classUrl = TestAnonymousClassUnloading.class.getResource(
TestAnonymousClassUnloading.class.getName().replace('.', '/') + ".class");
String rn = TestAnonymousClassUnloading.class.getSimpleName() + ".class";
URL classUrl = TestAnonymousClassUnloading.class.getResource(rn);
URLConnection connection = classUrl.openConnection();
int length = connection.getContentLength();
......
......@@ -74,7 +74,7 @@ public class ClassLoadUnloadTest {
List<String> argsList = new ArrayList<>();
Collections.addAll(argsList, args);
Collections.addAll(argsList, "-Xmn8m");
Collections.addAll(argsList, "-Dtest.classes=" + System.getProperty("test.classes","."));
Collections.addAll(argsList, "-Dtest.class.path=" + System.getProperty("test.class.path", "."));
Collections.addAll(argsList, ClassUnloadTestMain.class.getName());
return ProcessTools.createJavaProcessBuilder(argsList.toArray(new String[argsList.size()]));
}
......
......@@ -31,8 +31,10 @@ import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.stream.Stream;
public class ClassUnloadCommon {
public static class TestFailure extends RuntimeException {
......@@ -61,14 +63,45 @@ public class ClassUnloadCommon {
System.gc();
}
/**
* Creates a class loader that loads classes from {@code ${test.class.path}}
* before delegating to the system class loader.
*/
public static ClassLoader newClassLoader() {
String cp = System.getProperty("test.class.path", ".");
URL[] urls = Stream.of(cp.split(File.pathSeparator))
.map(Paths::get)
.map(ClassUnloadCommon::toURL)
.toArray(URL[]::new);
return new URLClassLoader(urls) {
@Override
public Class<?> loadClass(String cn, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(cn)) {
Class<?> c = findLoadedClass(cn);
if (c == null) {
try {
c = findClass(cn);
} catch (ClassNotFoundException e) {
c = getParent().loadClass(cn);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
};
}
static URL toURL(Path path) {
try {
return new URLClassLoader(new URL[] {
Paths.get(System.getProperty("test.classes",".") + File.separatorChar + "classes").toUri().toURL(),
}, null);
} catch (MalformedURLException e){
throw new RuntimeException("Unexpected URL conversion failure", e);
return path.toUri().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}
......@@ -432,3 +432,6 @@ a5506b425f1bf91530d8417b57360e5d89328c0c jdk-9+173
5f504872a75b71f2fb19299f0d1e3395cf32eaa0 jdk-10+12
e6c4f6ef717d104dba880e2dae538690c993b46f jdk-9+175
4540d6376f3ef22305cca546f85f9b2ce9a210c4 jdk-10+13
7a2bc0a80087b63c909df2af6ec7d9ef44e6d7f7 jdk-10+14
9f27d513658d5375b0e26846857d92563f279073 jdk-9+176
80acf577b7d0b886fb555c9916552844f6cc72af jdk-9+177
......@@ -749,12 +749,14 @@ ifeq ($(OPENJDK_TARGET_OS), windows)
$(BUILD_LIBJAWT): $(BUILD_LIBAWT)
$(JDK_OUTPUTDIR)/lib/$(LIBRARY_PREFIX)jawt$(STATIC_LIBRARY_SUFFIX): $(BUILD_LIBJAWT)
$(call LogInfo, Copying $(patsubst $(OUTPUT_ROOT)/%, %, $@))
$(call MakeDir, $(@D))
$(CP) $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjawt/$(LIBRARY_PREFIX)jawt$(STATIC_LIBRARY_SUFFIX) $@
$(eval $(call SetupCopyFiles, COPY_JAWT_LIB, \
FILES := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjawt/$(LIBRARY_PREFIX)jawt$(STATIC_LIBRARY_SUFFIX), \
DEST := $(SUPPORT_OUTPUTDIR)/modules_libs/$(MODULE), \
))
$(COPY_JAWT_LIB): $(BUILD_LIBJAWT)
TARGETS += $(JDK_OUTPUTDIR)/lib/$(LIBRARY_PREFIX)jawt$(STATIC_LIBRARY_SUFFIX)
TARGETS += $(COPY_JAWT_LIB)
else # OPENJDK_TARGET_OS not windows
......
......@@ -29,6 +29,7 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.Element;
import javax.lang.model.element.ModuleElement;
import com.sun.source.doctree.DocTree;
import jdk.javadoc.doclet.Taglet;
import static jdk.javadoc.doclet.Taglet.Location.*;
......@@ -62,7 +63,7 @@ public class ModuleGraph implements Taglet {
return "";
}
String moduleName = element.getSimpleName().toString();
String moduleName = ((ModuleElement) element).getQualifiedName().toString();
String imageFile = moduleName + "-graph.png";
int thumbnailHeight = -1;
String hoverImage = "";
......
......@@ -47,6 +47,7 @@ char *getPosixLocale(int cat) {
#define LOCALEIDLENGTH 128
char *getMacOSXLocale(int cat) {
const char* retVal = NULL;
char localeString[LOCALEIDLENGTH];
switch (cat) {
case LC_MESSAGES:
......@@ -74,73 +75,114 @@ char *getMacOSXLocale(int cat) {
}
CFRelease(languages);
retVal = languageString;
// Explicitly supply region, if there is none
char *hyphenPos = strchr(languageString, '-');
int langStrLen = strlen(languageString);
if (hyphenPos == NULL || // languageString contains ISO639 only, e.g., "en"
languageString + langStrLen - hyphenPos == 5) { // ISO639-ScriptCode, e.g., "en-Latn"
CFStringGetCString(CFLocaleGetIdentifier(CFLocaleCopyCurrent()),
localeString, LOCALEIDLENGTH, CFStringGetSystemEncoding());
char *underscorePos = strrchr(localeString, '_');
char *region = NULL;
if (underscorePos != NULL) {
region = underscorePos + 1;
}
// Special case for Portuguese in Brazil:
// The language code needs the "_BR" region code (to distinguish it
// from Portuguese in Portugal), but this is missing when using the
// "Portuguese (Brazil)" language.
// If language is "pt" and the current locale is pt_BR, return pt_BR.
char localeString[LOCALEIDLENGTH];
if (strcmp(retVal, "pt") == 0 &&
CFStringGetCString(CFLocaleGetIdentifier(CFLocaleCopyCurrent()),
localeString, LOCALEIDLENGTH, CFStringGetSystemEncoding()) &&
strcmp(localeString, "pt_BR") == 0) {
retVal = localeString;
if (region != NULL) {
strcat(languageString, "-");
strcat(languageString, region);
}
}
retVal = languageString;
}
break;
default:
{
char localeString[LOCALEIDLENGTH];
if (!CFStringGetCString(CFLocaleGetIdentifier(CFLocaleCopyCurrent()),
localeString, LOCALEIDLENGTH, CFStringGetSystemEncoding())) {
return NULL;
}
retVal = localeString;
}
break;
}
if (retVal != NULL) {
// Language IDs use the language designators and (optional) region
// and script designators of BCP 47. So possible formats are:
//
// "en" (language designator only)
// "haw" (3-letter lanuage designator)
// "en-GB" (language with alpha-2 region designator)
// "es-419" (language with 3-digit UN M.49 area code)
// "zh-Hans" (language with ISO 15924 script designator)
// "zh-Hans-US" (language with ISO 15924 script designator and region)
// "zh-Hans-419" (language with ISO 15924 script designator and UN M.49)
//
// In the case of region designators (alpha-2 and/or UN M.49), we convert
// to our locale string format by changing '-' to '_'. That is, if
// the '-' is followed by fewer than 4 chars.
char* scriptOrRegion = strchr(retVal, '-');
if (scriptOrRegion != NULL) {
int length = strlen(scriptOrRegion);
if (length > 5) {
// Region and script both exist. Honor the script for now
scriptOrRegion[5] = '\0';
} else if (length < 5) {
*scriptOrRegion = '_';
assert((length == 3 &&
// '-' followed by a 2 character region designator
isalpha(scriptOrRegion[1]) &&
isalpha(scriptOrRegion[2])) ||
(length == 4 &&
// '-' followed by a 3-digit UN M.49 area code
isdigit(scriptOrRegion[1]) &&
isdigit(scriptOrRegion[2]) &&
isdigit(scriptOrRegion[3])));
}
return strdup(convertToPOSIXLocale(retVal));
}
return NULL;
}
/* Language IDs use the language designators and (optional) region
* and script designators of BCP 47. So possible formats are:
*
* "en" (language designator only)
* "haw" (3-letter lanuage designator)
* "en-GB" (language with alpha-2 region designator)
* "es-419" (language with 3-digit UN M.49 area code)
* "zh-Hans" (language with ISO 15924 script designator)
* "zh-Hans-US" (language with ISO 15924 script designator and region)
* "zh-Hans-419" (language with ISO 15924 script designator and UN M.49)
*
* convert these tags into POSIX conforming locale string, i.e.,
* lang{_region}{@script}. e.g., for "zh-Hans-US" into "zh_US@Hans"
*/
const char * convertToPOSIXLocale(const char* src) {
char* scriptRegion = strchr(src, '-');
if (scriptRegion != NULL) {
int length = strlen(scriptRegion);
char* region = strchr(scriptRegion + 1, '-');
char* atMark = NULL;
if (region == NULL) {
// CFLocaleGetIdentifier() returns '_' before region
region = strchr(scriptRegion + 1, '_');
}
*scriptRegion = '_';
if (length > 5) {
// Region and script both exist.
char tmpScript[4];
int regionLength = length - 6;
atMark = scriptRegion + 1 + regionLength;
memcpy(tmpScript, scriptRegion + 1, 4);
memmove(scriptRegion + 1, region + 1, regionLength);
memcpy(atMark + 1, tmpScript, 4);
} else if (length == 5) {
// script only
atMark = scriptRegion;
}
if (atMark != NULL) {
*atMark = '@';
// assert script code
assert(isalpha(atMark[1]) &&
isalpha(atMark[2]) &&
isalpha(atMark[3]) &&
isalpha(atMark[4]));
}
return strdup(retVal);
assert(((length == 3 || length == 8) &&
// '_' followed by a 2 character region designator
isalpha(scriptRegion[1]) &&
isalpha(scriptRegion[2])) ||
((length == 4 || length == 9) &&
// '_' followed by a 3-digit UN M.49 area code
isdigit(scriptRegion[1]) &&
isdigit(scriptRegion[2]) &&
isdigit(scriptRegion[3])) ||
// '@' followed by a 4 character script code (already validated above)
(length == 5));
}
return NULL;
return src;
}
char *setupMacOSXLocale(int cat) {
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册