Skip to content

Commit 119617e

Browse files
committed
Fix unicode escapes in single-quoted strings
1 parent 6779577 commit 119617e

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

lib/graphql/language/lexer.rb

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ def debug_token_value(token_name)
107107
}
108108
UTF_8 = /\\u(?:([\dAa-f]{4})|\{([\da-f]{4,})\})(?:\\u([\dAa-f]{4}))?/i
109109
VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{UTF_8})*\z/o
110+
ESCAPED = /(?:#{ESCAPES}|#{UTF_8})/o
110111

111112
def string_value
112113
str = token_value
@@ -299,24 +300,27 @@ module ByteFor
299300
# Replace any escaped unicode or whitespace with the _actual_ characters
300301
# To avoid allocating more strings, this modifies the string passed into it
301302
def self.replace_escaped_characters_in_place(raw_string)
302-
raw_string.gsub!(ESCAPES, ESCAPES_REPLACE)
303-
raw_string.gsub!(UTF_8) do |_matched_str|
304-
codepoint_1 = ($1 || $2).to_i(16)
305-
codepoint_2 = $3
303+
raw_string.gsub!(ESCAPED) do |matched_str|
304+
if (res = ESCAPES_REPLACE[matched_str])
305+
res
306+
else
307+
codepoint_1 = ($1 || $2).to_i(16)
308+
codepoint_2 = $3
306309

307-
if codepoint_2
308-
codepoint_2 = codepoint_2.to_i(16)
309-
if (codepoint_1 >= 0xD800 && codepoint_1 <= 0xDBFF) && # leading surrogate
310-
(codepoint_2 >= 0xDC00 && codepoint_2 <= 0xDFFF) # trailing surrogate
311-
# A surrogate pair
312-
combined = ((codepoint_1 - 0xD800) * 0x400) + (codepoint_2 - 0xDC00) + 0x10000
313-
[combined].pack('U'.freeze)
310+
if codepoint_2
311+
codepoint_2 = codepoint_2.to_i(16)
312+
if (codepoint_1 >= 0xD800 && codepoint_1 <= 0xDBFF) && # leading surrogate
313+
(codepoint_2 >= 0xDC00 && codepoint_2 <= 0xDFFF) # trailing surrogate
314+
# A surrogate pair
315+
combined = ((codepoint_1 - 0xD800) * 0x400) + (codepoint_2 - 0xDC00) + 0x10000
316+
[combined].pack('U'.freeze)
317+
else
318+
# Two separate code points
319+
[codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze)
320+
end
314321
else
315-
# Two separate code points
316-
[codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze)
322+
[codepoint_1].pack('U'.freeze)
317323
end
318-
else
319-
[codepoint_1].pack('U'.freeze)
320324
end
321325
end
322326
nil

spec/graphql/types/string_spec.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,33 @@ def self.type_error(err, ctx)
101101
assert_nil string_type.coerce_isolated_input(0.999)
102102
end
103103
end
104+
105+
106+
describe "unicode escapes" do
107+
class UnicodeEscapeSchema < GraphQL::Schema
108+
class QueryType < GraphQL::Schema::Object
109+
field :get_string, String do
110+
argument :string, String
111+
end
112+
113+
def get_string(string:)
114+
string
115+
end
116+
end
117+
118+
query(QueryType)
119+
end
120+
121+
it "parses escapes properly in single-quoted strings" do
122+
query_str = File.read("./spec/fixtures/unicode_escapes/query1.graphql")
123+
res = UnicodeEscapeSchema.execute(query_str)
124+
assert_equal "d", res["data"]["example1"]
125+
assert_equal "\\u0064", res["data"]["example2"]
126+
assert_equal "\\u006", res["data"]["example4"]
127+
128+
error_query_str = query_str.gsub(" # ", " ")
129+
res2 = UnicodeEscapeSchema.execute(error_query_str)
130+
assert_equal ["Expected string or block string, but it was malformed"], res2["errors"].map { |err| err["message"] }
131+
end
132+
end
104133
end

0 commit comments

Comments
 (0)