From 8f587a8aa5230e0b9c2a9cfd009c80c8b7d85c7c Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 12 Sep 2018 20:48:44 -0700 Subject: [PATCH] Bug 1491137 - Split the JS::Compile that takes const char*/size_t into JS::CompileUtf8 and JS::CompileLatin1 that assert |options.utf8| has consistent value, and a JS::Compile that delegates to one of those as appropriate -- a step toward having entirely separate compilation functions that do direct parsing of UTF-8 without inflating first. r=jandem --HG-- extra : rebase_source : 4fbc10a7e5e221da4c68c02dc155d925e7a7367d --- js/public/CompilationAndEvaluation.h | 70 +++++++++++++++++-- .../jsapi-tests/testCompileNonSyntactic.cpp | 2 +- .../testExecuteInJSMEnvironment.cpp | 4 +- js/src/vm/CompilationAndEvaluation.cpp | 67 +++++++++++++----- js/xpconnect/loader/mozJSComponentLoader.cpp | 8 +-- js/xpconnect/loader/mozJSSubScriptLoader.cpp | 4 +- 6 files changed, 126 insertions(+), 29 deletions(-) diff --git a/js/public/CompilationAndEvaluation.h b/js/public/CompilationAndEvaluation.h index d5fb09ebf1b9..7a293830a133 100644 --- a/js/public/CompilationAndEvaluation.h +++ b/js/public/CompilationAndEvaluation.h @@ -86,7 +86,7 @@ JS_ExecuteScript(JSContext* cx, JS::AutoVector& envChain, JS::Handle< * |script| will always be set. On failure, it will be set to nullptr. */ extern JS_PUBLIC_API(bool) -JS_CompileScript(JSContext* cx, const char* ascii, size_t length, +JS_CompileScript(JSContext* cx, const char* bytes, size_t length, const JS::CompileOptions& options, JS::MutableHandle script); @@ -152,9 +152,63 @@ extern JS_PUBLIC_API(bool) Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, MutableHandle script); +/** + * Compile the provided UTF-8 data into a script. If the data contains invalid + * UTF-8, an error is reported. + * + * The |options.utf8| flag is asserted to be true. At a future time this flag + * will be removed and UTF-8-ness of source bytes will be entirely determined + * by which compilation function is called. + * + * |script| is always set to the compiled script or to null in case of error. + */ extern JS_PUBLIC_API(bool) +CompileUtf8(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, MutableHandle script); + +/** + * Compile the provided Latin-1 data (i.e. each byte directly corresponds to + * the same Unicode code point) into a script. + * + * The |options.utf8| flag is asserted to be false. At a future time this flag + * will be removed and UTF-8-ness of source bytes will be entirely determined + * by which compilation function is called. + * + * This function may eventually be removed, such that *only* bytes containing + * UTF-8 source text may be directly compiled. Avoid using it if you can. + * + * |script| is always set to the compiled script or to null in case of error. + */ +extern JS_PUBLIC_API(bool) +CompileLatin1(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, MutableHandle script); + +/** + * DEPRECATED + * + * Compile the provided bytes into a script. + * + * If |options.utf8|, the bytes are interpreted as UTF-8 data. If the data + * contains any malformed UTF-8, an error is reported. + * + * Otherwise they are interpreted as Latin-1, i.e. each byte directly + * corresponds to the same Unicode code point. + * + * |script| is always set to the compiled script or to null in case of error. + * + * Do not use this API. The JS::CompileOptions::utf8 flag that indicates how + * to interpret |bytes| is currently being replaced by functions indicating an + * exact expected encoding. If you have byte data to compile, you should use + * either JS::CompileUtf8 or JS::CompileLatin1, as appropriate. + */ +inline bool Compile(JSContext* cx, const ReadOnlyCompileOptions& options, - const char* bytes, size_t length, MutableHandle script); + const char* bytes, size_t length, MutableHandle script) +{ + return options.utf8 + ? CompileUtf8(cx, options, bytes, length, script) + : CompileLatin1(cx, options, bytes, length, script); +} extern JS_PUBLIC_API(bool) Compile(JSContext* cx, const ReadOnlyCompileOptions& options, @@ -168,9 +222,17 @@ extern JS_PUBLIC_API(bool) CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, MutableHandle script); +/** + * Compile the given Latin-1 data for non-syntactic scope. + * + * There is no way to compile UTF-8 data for non-syntactic scope because no + * user currently needs it. Such way could be added in the future if it's ever + * needed. + */ extern JS_PUBLIC_API(bool) -CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, - const char* bytes, size_t length, MutableHandle script); +CompileLatin1ForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, + MutableHandle script); extern JS_PUBLIC_API(bool) CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, diff --git a/js/src/jsapi-tests/testCompileNonSyntactic.cpp b/js/src/jsapi-tests/testCompileNonSyntactic.cpp index 061541e7baae..7022808ff385 100644 --- a/js/src/jsapi-tests/testCompileNonSyntactic.cpp +++ b/js/src/jsapi-tests/testCompileNonSyntactic.cpp @@ -85,7 +85,7 @@ testCompile(bool nonSyntactic) CHECK(CompileForNonSyntacticScope(cx, options, buf, &script)); CHECK_EQUAL(script->hasNonSyntacticScope(), true); - CHECK(CompileForNonSyntacticScope(cx, options, src, length, &script)); + CHECK(CompileLatin1ForNonSyntacticScope(cx, options, src, length, &script)); CHECK_EQUAL(script->hasNonSyntacticScope(), true); { diff --git a/js/src/jsapi-tests/testExecuteInJSMEnvironment.cpp b/js/src/jsapi-tests/testExecuteInJSMEnvironment.cpp index 31b11a89025d..b981211b26f1 100644 --- a/js/src/jsapi-tests/testExecuteInJSMEnvironment.cpp +++ b/js/src/jsapi-tests/testExecuteInJSMEnvironment.cpp @@ -28,7 +28,7 @@ BEGIN_TEST(testExecuteInJSMEnvironment_Basic) options.setNoScriptRval(true); JS::RootedScript script(cx); - CHECK(JS::CompileForNonSyntacticScope(cx, options, src, sizeof(src)-1, &script)); + CHECK(JS::CompileLatin1ForNonSyntacticScope(cx, options, src, sizeof(src) - 1, &script)); JS::RootedObject varEnv(cx, js::NewJSMEnvironment(cx)); JS::RootedObject lexEnv(cx, JS_ExtensibleLexicalEnvironment(varEnv)); @@ -88,7 +88,7 @@ BEGIN_TEST(testExecuteInJSMEnvironment_Callback) options.setNoScriptRval(true); JS::RootedScript script(cx); - CHECK(JS::CompileForNonSyntacticScope(cx, options, src, sizeof(src)-1, &script)); + CHECK(JS::CompileLatin1ForNonSyntacticScope(cx, options, src, sizeof(src) - 1, &script)); JS::RootedObject nsvo(cx, js::NewJSMEnvironment(cx)); CHECK(nsvo); diff --git a/js/src/vm/CompilationAndEvaluation.cpp b/js/src/vm/CompilationAndEvaluation.cpp index 1be2e67fd7ce..d4e898c1a3ce 100644 --- a/js/src/vm/CompilationAndEvaluation.cpp +++ b/js/src/vm/CompilationAndEvaluation.cpp @@ -9,6 +9,9 @@ #include "js/CompilationAndEvaluation.h" #include "mozilla/Maybe.h" // mozilla::None, mozilla::Some +#include "mozilla/TextUtils.h" // mozilla::IsAscii + +#include // std::all_of #include "jstypes.h" // JS_PUBLIC_API @@ -50,15 +53,27 @@ Compile(JSContext* cx, const ReadOnlyCompileOptions& options, } static bool -Compile(JSContext* cx, const ReadOnlyCompileOptions& options, - const char* bytes, size_t length, JS::MutableHandleScript script) +CompileLatin1(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, JS::MutableHandleScript script) { - char16_t* chars; - if (options.utf8) { - chars = JS::UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get(); - } else { - chars = InflateString(cx, bytes, length); + MOZ_ASSERT(!options.utf8); + + char16_t* chars = InflateString(cx, bytes, length); + if (!chars) { + return false; } + + SourceBufferHolder source(chars, length, SourceBufferHolder::GiveOwnership); + return ::Compile(cx, options, source, script); +} + +static bool +CompileUtf8(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, JS::MutableHandleScript script) +{ + MOZ_ASSERT(options.utf8); + + char16_t* chars = JS::UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get(); if (!chars) { return false; } @@ -76,7 +91,12 @@ Compile(JSContext* cx, const ReadOnlyCompileOptions& options, return false; } - return ::Compile(cx, options, reinterpret_cast(buffer.begin()), buffer.length(), script); + return options.utf8 + ? ::CompileUtf8(cx, options, + reinterpret_cast(buffer.begin()), buffer.length(), script) + : ::CompileLatin1(cx, options, + reinterpret_cast(buffer.begin()), buffer.length(), + script); } static bool @@ -100,10 +120,19 @@ JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, } bool -JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, - const char* bytes, size_t length, JS::MutableHandleScript script) +JS::CompileLatin1(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, JS::MutableHandleScript script) { - return ::Compile(cx, options, bytes, length, script); + MOZ_ASSERT(!options.utf8); + return ::CompileLatin1(cx, options, bytes, length, script); +} + +bool +JS::CompileUtf8(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, JS::MutableHandleScript script) +{ + MOZ_ASSERT(options.utf8); + return ::CompileUtf8(cx, options, bytes, length, script); } bool @@ -130,12 +159,16 @@ JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& opt } bool -JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, - const char* bytes, size_t length, JS::MutableHandleScript script) +JS::CompileLatin1ForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, + const char* bytes, size_t length, + JS::MutableHandleScript script) { + MOZ_ASSERT(!optionsArg.utf8, + "this function only compiles Latin-1 source text"); + CompileOptions options(cx, optionsArg); options.setNonSyntacticScope(true); - return ::Compile(cx, options, bytes, length, script); + return ::CompileLatin1(cx, options, bytes, length, script); } bool @@ -157,10 +190,12 @@ JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& opt } JS_PUBLIC_API(bool) -JS_CompileScript(JSContext* cx, const char* ascii, size_t length, +JS_CompileScript(JSContext* cx, const char* bytes, size_t length, const JS::CompileOptions& options, MutableHandleScript script) { - return ::Compile(cx, options, ascii, length, script); + return options.utf8 + ? ::CompileUtf8(cx, options, bytes, length, script) + : ::CompileLatin1(cx, options, bytes, length, script); } JS_PUBLIC_API(bool) diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index 6f6c36455e6f..868f31f03761 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -879,18 +879,18 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo, // don't early return for them here. auto buf = map.get(); if (reuseGlobal) { - CompileForNonSyntacticScope(cx, options, buf.get(), map.size(), &script); + CompileLatin1ForNonSyntacticScope(cx, options, buf.get(), map.size(), &script); } else { - Compile(cx, options, buf.get(), map.size(), &script); + CompileLatin1(cx, options, buf.get(), map.size(), &script); } } else { nsCString str; MOZ_TRY_VAR(str, ReadScript(aInfo)); if (reuseGlobal) { - CompileForNonSyntacticScope(cx, options, str.get(), str.Length(), &script); + CompileLatin1ForNonSyntacticScope(cx, options, str.get(), str.Length(), &script); } else { - Compile(cx, options, str.get(), str.Length(), &script); + CompileLatin1(cx, options, str.get(), str.Length(), &script); } } // Propagate the exception, if one exists. Also, don't leave the stale diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp index bc571f25dc1a..128e9fc9dee8 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -178,9 +178,9 @@ PrepareScript(nsIURI* uri, // the lazy source loader doesn't know the encoding. options.setSourceIsLazy(true); if (wantGlobalScript) { - return JS::Compile(cx, options, buf, len, script); + return JS::CompileLatin1(cx, options, buf, len, script); } - return JS::CompileForNonSyntacticScope(cx, options, buf, len, script); + return JS::CompileLatin1ForNonSyntacticScope(cx, options, buf, len, script); } static bool