/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.template.soy.jssrc.internal; import com.google.common.collect.ImmutableSet; import com.google.template.soy.base.internal.BaseUtils; /** * Shared utilities specific to the JS Src backend. * * <p>Important: Do not use outside of Soy code (treat as superpackage-private). * */ public final class JsSrcUtils { private JsSrcUtils() {} /** * Builds a version of the given string that has literal Unicode Format characters (Unicode * category "Cf") changed to valid JavaScript Unicode escapes (i.e. &92;u####). If the provided * string doesn't have any Unicode Format characters, then the same string is returned. * * @param str The string to escape. * @return A version of the given string that has literal Unicode Format characters (Unicode * category "Cf") changed to valid JavaScript Unicode escapes (i.e. &92;u####). */ static String escapeUnicodeFormatChars(String str) { int len = str.length(); // Do a quick check first, because most strings do not contain Unicode format characters. boolean hasFormatChar = false; for (int i = 0; i < len; i++) { if (Character.getType(str.charAt(i)) == Character.FORMAT) { hasFormatChar = true; break; } } if (!hasFormatChar) { return str; } // Now we actually need to build a new string. StringBuilder out = new StringBuilder(len * 4 / 3); int codePoint; for (int i = 0; i < len; i += Character.charCount(codePoint)) { codePoint = str.codePointAt(i); if (Character.getType(codePoint) == Character.FORMAT) { BaseUtils.appendHexEscape(out, codePoint); } else { out.appendCodePoint(codePoint); } } return out.toString(); } /** * Returns true if key is a JavaScript reserved word. * * <p>TODO(lukes): rename to 'needs quoting for property access' and move callers using this for * local variables to use the name generator instead. */ static boolean isReservedWord(String key) { return LEGACY_JS_RESERVED_WORDS.contains(key); } static final ImmutableSet<String> JS_LITERALS = ImmutableSet.of("null", "true", "false", "NaN", "Infinity", "undefined"); static final ImmutableSet<String> JS_RESERVED_WORDS = ImmutableSet.of( "break", "case", "catch", "continue", "debugger", "default", "delete", "do", "else", "finally", "for", "function", "if", "in", "instanceof", "new", "return", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with", "class", "const", "enum", "export", "extends", "import", "super", "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield", /* future reserved words */ "async", "await"); /** * Set of words that JavaScript considers reserved words. These words cannot be used as * identifiers. This list is from the ECMA-262 v5, section 7.6.1: * http://www.ecma-international.org/publications/files/drafts/tc39-2009-050.pdf plus the keywords * for boolean values and {@code null}. (Also includes the identifiers "soy" and "soydata" which * are used internally by Soy.) */ private static final ImmutableSet<String> LEGACY_JS_RESERVED_WORDS = ImmutableSet.<String>builder() .addAll(JS_LITERALS) .addAll(JS_RESERVED_WORDS) .add("soy") .add("soydata") .build(); }