'use strict';
-function GetTemplateCallSite(siteObj, rawStrings) {
- // TODO(caitp): ensure same template callsite is used for subsequent tag calls
+var callSiteCache = new $Map;
+
+function SameCallSiteElements(rawStrings, other) {
+ var length = rawStrings.length;
+ var other = other.raw;
+
+ if (length !== other.length) return false;
+
+ for (var i = 0; i < length; ++i) {
+ if (rawStrings[i] !== other[i]) return false;
+ }
+
+ return true;
+}
+
+
+function GetCachedCallSite(siteObj, hash) {
+ var obj = %MapGet(callSiteCache, hash);
+
+ if (IS_UNDEFINED(obj)) return;
+
+ var length = obj.length;
+ for (var i = 0; i < length; ++i) {
+ if (SameCallSiteElements(siteObj, obj[i])) return obj[i];
+ }
+}
+
+
+function SetCachedCallSite(siteObj, hash) {
+ var obj = %MapGet(callSiteCache, hash);
+ var array;
+
+ if (IS_UNDEFINED(obj)) {
+ array = new InternalArray(1);
+ array[0] = siteObj;
+ %MapSet(callSiteCache, hash, array);
+ } else {
+ obj.push(siteObj);
+ }
+
+ return siteObj;
+}
+
+
+function GetTemplateCallSite(siteObj, rawStrings, hash) {
+ var cached = GetCachedCallSite(rawStrings, hash);
+
+ if (!IS_UNDEFINED(cached)) return cached;
%AddNamedProperty(siteObj, "raw", %ObjectFreeze(rawStrings),
READ_ONLY | DONT_ENUM | DONT_DELETE);
- return %ObjectFreeze(siteObj);
+ return SetCachedCallSite(%ObjectFreeze(siteObj), hash);
}
}
return expr;
} else {
- ZoneList<Expression*>* raw_strings = TemplateRawStrings(lit);
+ uint32_t hash;
+ ZoneList<Expression*>* raw_strings = TemplateRawStrings(lit, &hash);
Handle<String> source(String::cast(script()->source()));
int cooked_idx = function_state_->NextMaterializedLiteralIndex();
factory()->NewArrayLiteral(
const_cast<ZoneList<Expression*>*>(raw_strings), raw_idx, pos),
zone());
+
+ // Ensure hash is suitable as an Smi value
+ Smi* hash_obj = Smi::cast(Internals::IntToSmi(static_cast<int>(hash)));
+ args->Add(factory()->NewSmiLiteral(hash_obj->value(), pos), zone());
+
this->CheckPossibleEvalCall(tag, scope_);
Expression* call_site = factory()->NewCallRuntime(
ast_value_factory()->get_template_callsite_string(), NULL, args, start);
}
-ZoneList<Expression*>* Parser::TemplateRawStrings(const TemplateLiteral* lit) {
+ZoneList<Expression*>* Parser::TemplateRawStrings(const TemplateLiteral* lit,
+ uint32_t* hash) {
const ZoneList<int>* lengths = lit->lengths();
const ZoneList<Expression*>* cooked_strings = lit->cooked();
int total = lengths->length();
raw_strings = new (zone()) ZoneList<Expression*>(total, zone());
+ int num_hash_chars = (total - 1) * 3;
+ for (int index = 0; index < total; ++index) {
+ // Allow about length * 4 to handle most UTF8 sequences.
+ num_hash_chars += lengths->at(index) * 4;
+ }
+
+ Vector<uint8_t> hash_string = Vector<uint8_t>::New(num_hash_chars);
+ num_hash_chars = 0;
+
for (int index = 0; index < total; ++index) {
int span_start = cooked_strings->at(index)->position() + 1;
int span_end = lengths->at(index) - 1;
int length;
int to_index = 0;
+ if (index) {
+ hash_string[num_hash_chars++] = '$';
+ hash_string[num_hash_chars++] = '{';
+ hash_string[num_hash_chars++] = '}';
+ }
+
SmartArrayPointer<char> raw_chars =
source->ToCString(ALLOW_NULLS, FAST_STRING_TRAVERSAL, span_start,
span_end, &length);
++from_index;
}
}
+ hash_string[num_hash_chars++] = ch;
raw_chars[to_index++] = ch;
}
raw_strings->Add(raw_lit, zone());
}
+ hash_string.Truncate(num_hash_chars);
+ int utf16_length;
+ *hash = StringHasher::ComputeUtf8Hash(Vector<const char>::cast(hash_string),
+ num_hash_chars, &utf16_length);
+ hash_string.Dispose();
+
return raw_strings;
}
} } // namespace v8::internal
Expression* expression);
Expression* CloseTemplateLiteral(TemplateLiteralState* state, int start,
Expression* tag);
- ZoneList<Expression*>* TemplateRawStrings(const TemplateLiteral* lit);
+ ZoneList<Expression*>* TemplateRawStrings(const TemplateLiteral* lit,
+ uint32_t* hash);
Scanner scanner_;
PreParser* reusable_preparser_;
Scope* original_scope_; // for ES5 function declarations in sloppy eval
})();
+(function testCallSiteCaching() {
+ var callSites = [];
+ function tag(cs) { callSites.push(cs); }
+ var a = 1;
+ var b = 2;
+
+ tag`head${a}tail`;
+ tag`head${b}tail`;
+
+ assertEquals(2, callSites.length);
+ assertSame(callSites[0], callSites[1]);
+
+ eval("tag`head${a}tail`");
+ assertEquals(3, callSites.length);
+ assertSame(callSites[1], callSites[2]);
+
+ eval("tag`head${b}tail`");
+ assertEquals(4, callSites.length);
+ assertSame(callSites[2], callSites[3]);
+
+ (new Function("tag", "a", "b", "return tag`head${a}tail`;"))(tag, 1, 2);
+ assertEquals(5, callSites.length);
+ assertSame(callSites[3], callSites[4]);
+
+ (new Function("tag", "a", "b", "return tag`head${b}tail`;"))(tag, 1, 2);
+ assertEquals(6, callSites.length);
+ assertSame(callSites[4], callSites[5]);
+
+ callSites = [];
+
+ tag`foo${a}bar`;
+ tag`foo\${.}bar`;
+ assertEquals(2, callSites.length);
+ assertEquals(2, callSites[0].length);
+ assertEquals(1, callSites[1].length);
+
+ callSites = [];
+
+ eval("tag`\\\r\n\\\n\\\r`");
+ eval("tag`\\\r\n\\\n\\\r`");
+ assertEquals(2, callSites.length);
+ assertSame(callSites[0], callSites[1]);
+ assertEquals("", callSites[0][0]);
+ assertEquals("\\\n\\\n\\\n", callSites[0].raw[0]);
+
+ callSites = [];
+
+ tag`\uc548\ub155`;
+ tag`\uc548\ub155`;
+ assertEquals(2, callSites.length);
+ assertSame(callSites[0], callSites[1]);
+ assertEquals("안녕", callSites[0][0]);
+ assertEquals("\\uc548\\ub155", callSites[0].raw[0]);
+
+ callSites = [];
+
+ tag`\uc548\ub155`;
+ tag`안녕`;
+ assertEquals(2, callSites.length);
+ assertTrue(callSites[0] !== callSites[1]);
+ assertEquals("안녕", callSites[0][0]);
+ assertEquals("\\uc548\\ub155", callSites[0].raw[0]);
+ assertEquals("안녕", callSites[1][0]);
+ // TODO(caitp, arv): blocked on correctly generating raw strings from
+ // multi-byte UTF8.
+ // assertEquals("안녕", callSites[1].raw[0]);
+})();
+
+
(function testExtendedArrayPrototype() {
Object.defineProperty(Array.prototype, 0, {
set: function() {