为什么相爱的人却不能在一起| 91是什么东西| 心率高有什么危害| 五彩缤纷是什么意思| 歇夏是什么意思| 这个字念什么| 心内科是看什么病的| 操逼什么意思| 暑湿感冒吃什么药| 月子里吃什么饭最好| 梦见和老公结婚是什么意思| 山开念什么| 尿酸高吃什么中药| 失眠为什么开奥氮平片| 麦粒肿吃什么消炎药| 什么地移入| 纳呆什么意思| 加拿大用什么货币| 里长是什么官| 布洛芬治什么| 女人五行缺水是什么命| 卢字五行属什么| 今天什么时辰立秋| 月经过后有褐色分泌物是什么原因| l5s1椎间盘突出是什么意思| 饺子是什么意思| 自贸区什么意思| 一月19日是什么星座| 表面是什么意思| 宝宝爱出汗是什么原因| AC是胎儿的什么意思| 一箭双雕是什么意思| 乙状结肠管状腺瘤是什么意思| 刮痧用什么油刮最好| 什么是速写| 知趣是什么意思| 梦见蛇在家里是什么意思| 坚果是什么| 盐酸是什么| 天衣无缝是什么意思| 耳道炎是什么原因引起的| 骶椎隐裂是什么意思| 小狗需要打什么疫苗| 微信上面有个耳朵是什么意思| 80岁是什么之年| mm代表什么| acg文化是什么意思| 鼻窦炎是什么原因引起的呢| 下肢静脉血栓挂什么科| 桃胶有什么功效与作用| 夜猫子是什么意思| 迷走神经是什么| 手足情深什么意思| ce是什么元素| 才情是什么意思| 手机五行属什么| 毒龙钻什么意思| 老人家脚肿是什么原因引起的| 猫能吃什么人吃的东西| 胃溃疡吃什么食物好| 打醮是什么意思| 心脏不好的人吃什么好| 为什么最迷人的最危险是什么歌| 什么手表品牌最好| cno什么意思| 00后是什么意思| 肝内血管瘤是什么意思| 出虚汗是什么原因| 中产阶级的标准是什么| 怀孕上火吃什么能降火| 全身痒是什么病的前兆| 甲是什么生肖| puppies什么意思| 冬阴功汤是什么味道| 查血型挂什么科| 肉是什么意思| 吃什么降血压| 蛇的尾巴有什么作用| 玮五行属什么| 农历闰月有什么规律| 二婚结婚需要什么证件| 牙齿上有黄斑是什么原因| 被马蜂蛰了用什么药| 为什么会出现眼袋| 春梦是什么意思| 医院为什么禁止小孩灌肠| 75岁属什么| colorful是什么牌子| 脑白质变性什么意思| 腰椎间盘膨出是什么意思| 莫名其妙的名是什么意思| 一刻是什么意思| 脑梗是什么原因引起的| 铁锭是什么意思| 为什么经常头疼| 丛林法则是什么意思| 酒后头疼什么原因| newbee什么意思| 月经两个月没来是什么原因| 嗓子发苦是什么原因| 什么是溃疡| 手心发热吃什么药| 急性牙髓炎吃什么药| 杜鹃花什么时候开| 舌头溃疡是什么原因| 经常腹痛什么原因| 胃间质瘤是什么性质的瘤| 看空是什么意思| 乳房硬块疼是什么原因| 肉桂和桂皮有什么区别| 打耳洞需要注意什么| 月经腰疼是什么原因引起的| 什么烟好抽又便宜| 梦到蜈蚣是什么意思| otc属于什么药| 美商是什么意思| 吃了安宫牛黄丸要禁忌什么不能吃| 每逢佳节倍思亲的上一句是什么| 555是什么烟| 5.16是什么星座| 颈椎病最怕干什么活| 子宫内膜息肉样增生是什么意思| 肠道问题挂什么科| 虹字五行属什么| 阿司匹林不能和什么药一起吃| 什么牌子的风扇好| 利口酒是什么酒| 华丽的近义词是什么| 沈阳有什么好玩的地方| 狼吞虎咽什么意思| 小水滴会变成什么| 心肌炎是什么症状| 血糖查什么项目| 鲤鱼喜欢吃什么| 静待花开的前一句是什么| 什么什么桑田| 尿很臭是什么原因| 吃什么能让月经量增多| 窦炎症是什么病| 脾胃虚吃什么水果好| 47年属什么生肖| 阴人是什么意思| 晚上五点是什么时辰| omega是什么牌子的手表| 96年出生的属什么| 女人什么时候是排卵期| cd4是什么意思| 十一月二十五是什么星座| 痰核流注什么意思| 口业是什么意思| 什么故事| oversize风格什么意思| 什么时候测试怀孕最准确的| 什么思而行| 老是低血糖是什么原因| 乙肝e抗体高是什么意思| 当兵什么兵种最好| 农历六月十三是什么星座| 满城尽带黄金甲是什么意思| 上颚起泡是什么原因| preparing是什么意思| ups是什么快递公司| 女男是什么字| 梦见前男友结婚了是什么征兆| 胎毛什么时候剃最好| 浓缩汁是什么意思| 睡觉被口水呛醒是什么原因| 副营级是什么军衔| burberry什么牌子| 故意不接电话说明什么| 吃什么食物能补钾| 护理学是什么| 狮子座和什么星座不合| 1963年属兔的是什么命| 附件炎是什么原因引起的| 榴莲对孕妇有什么好处| 什么地问填词语| 女人经期吃什么食物好| 嗔恨心是什么意思| 87属什么生肖| 哮喘吃什么药最好| 寻的部首是什么| 裕字五行属什么| 尿道炎用什么药治疗| 老虎五行属什么| 移花接木什么意思| 花椒什么时候成熟| 血小板高是什么问题| 二甲苯是什么| 双鱼座的幸运色是什么颜色| 乾隆为什么不喜欢雍正| 食管炎吃什么药| sd值是什么意思| 湿气重吃什么调理| 千什么一发| 副县长是什么级别| 心脏彩超ef是什么意思| 内痔疮用什么药治最好效果最快| 囊性病变是什么意思| 新生儿湿肺是什么意思| 什么药物过量会致死| 属龙和什么属相相冲| 流产可以吃什么水果| 鹅口疮有什么症状| 炼乳是什么东西| 枸杞和什么一起泡水喝最好| 什么风什么面| 天蓝色配什么颜色| 没晨勃说明什么问题| 7.23什么星座| 什么拜之交| 毛戈平化妆品什么档次| 菏泽有什么好玩的地方| 为什么健身后体重反而重了| 轮状病毒是什么症状| 手指尖麻木是什么原因| 支气管炎哮喘吃什么药| 冉字五行属什么| 全身发烫但不发烧是什么原因| 孙五行属什么| 宫颈炎用什么药物治疗比较好| 39属什么| 什么是高脂血症| 牛肉配什么菜好吃| 茄子和什么不能一起吃| 西楼是什么意思| 嗜酸性肉芽肿是什么病| 便秘是什么症状| 呀啦嗦是什么意思| 板鞋配什么裤子好看| eagle是什么意思| 蒲公英和什么相克致死| 股票缺口是什么意思| 食道挂什么科| 梦见丢了一只鞋是什么意思| 盘核桃有什么好处| 梦到被猪咬是什么意思| 提成是什么意思| 脑梗会有什么后遗症| 体态是什么意思| 女生的胸长什么样| 脑出血什么原因引起的| ck什么意思| 1943年属羊的是什么命| 金骏眉是什么茶类| 6月19是什么星座| 鼻尖发红是什么原因| 什么是面瘫| 胃疼喝什么能缓解疼痛| 伤口拆线挂什么科| 上颚疼吃什么药| 九零年属什么生肖| 紫癜是什么病严重吗| 为什么文化大革命| 牡丹花什么时候开花| 什么是低碳饮食| 西多士是什么| 主动脉夹层什么意思| 紫色心情是什么意思| 入殓师是做什么的| 家里进蝙蝠什么预兆| 备孕需要检查什么| 血糖高做什么运动好| 吃什么不会胖又减肥| 百度

各地政府网站、重点新闻网站、重点报纸电子版导航

Proper Tail Calls (PTC) is a new feature in the ECMAScript 6 language. This feature was added to facilitate recursive programming patterns, both for direct and indirect recursion. Various other design patterns can benefit from PTC as well, such as code that wraps some functionality where the wrapping code directly returns the result of what it wraps. Through the use of PTC, the amount of memory needed to run code is reduced. In deeply recursive code, PTC enables code to run that would otherwise throw a stack overflow exception.

What is a Proper Tail Call?

Typically when calling a function, stack space is allocated for the data associated with making a function call. This data includes the return address, prior stack pointer, arguments to the function, and space for the function’s local values. This space is called a stack frame. A call made to a function in tail position will reuse the stack space of the calling function. A function call is in tail position if the following criteria are met:

  • The calling function is in strict mode.
  • The calling function is either a normal function or an arrow function.
  • The calling function is not a generator function.
  • The return value of the called function is returned by the calling function.

When a function call is in tail position, ECMAScript 6 mandates that such a call must reuse the stack space of its own frame instead of pushing another frame onto the call stack. To emphasize, ECMAScript 6 requires that a call in tail position will reuse the caller’s stack space. The calling function’s frame is called a tail deleted frame as it is no longer on the stack once it makes a tail call. This means that the tail deleted function will not show up in a stack trace. It is important to note that PTC differs from Tail Call Optimization, which is a discretionary optimization that many optimizing compilers will make for various performance reasons.

Benefits of Proper Tail Calls

PTC was added to ECMAScript primarily to reuse stack space. The reuse of the stack memory allows for recursive and tail call coding patterns common to functional programming and other programming paradigms. Using PTC, a program could make an unbounded number of consecutive tail calls without unboundedly growing the stack.

PTC provides other benefits as well. Programs that utilize PTC can see a reduced memory footprint because the garbage collector will be more likely to collect certain local objects. Programs that utilize PTC can also see an improvement in speed because there is less processing when returning from a tail called function.

Stack Space

Reduced stack usage can provide benefits in other ways as well. Modern computing devices incorporate tiered memory caches to reduce latency in memory accesses. Although these caches are generous in size, they are still finite. Reducing stack usage through the use of PTC also reduces the amount of cache space needed, freeing up cache space for other memory accesses.

Locally Allocated Objects

Consider a function that allocates a local object, but that object is never made visible to other code. The only references to such a local object will be through a pointer in the function’s stack frame or in a register that the function is using. Should the JavaScript virtual machine need to garbage collect memory, it will find a reference to such a local object by scanning the stack and the contents of the CPU’s registers. If that function makes a call to another function and that call is not a tail call, then any local objects of the calling function will not be collected until the calling function itself returns. However, if a function makes a tail call to another function, all local objects of the calling function can be garbage collected because there are no more stack references to the object.

Returning from a Tail Called Function

Another benefit of PTC is that when a leaf function returns, it bypasses all intermediate tail called functions and returns directly to the first caller that didn’t make a tail call. This eliminates all of the return processing of those intermediate functions. The deeper the call chain of successive tail calls, the more performance benefit this provides. This works for both direct and mutual recursion.

Examples

There are many algorithms that are best written using recursion. Many of those algorithms naturally take advantage of PTC, while others may require some reworking. Consider writing a program to compute the greatest common divisor (GCD) function using Euclid’s algorithm. The translation of Euclid’s algorithm into a program that utilizes PTC is simple, elegant, and natural:

"use strict";
function gcd(m, n)
{
    if (!n)
        return m;
    return gcd(n, m % n);
}

The natural translation of other recursive mathematical functions can lead to recursive calls that are not in tail position. For example, a program that computes factorial (N!) is commonly written as:

"use strict";
function factorial(n)
{
    if (!n)
        return 1;
    return n * factorial(n - 1);
}

In this function, the recursive call to factorial() is not in tail position because the return statement computes and returns the product of n and the result of the recursive call. As a reminder, to be in tail position, the return value of the called function must be the only thing returned by the calling function. With a little modification, we can rewrite factorial to utilize PTC as follows:

"use strict";
function factorial(n, partialFactorial = 1)
{
    if (!n)
        return partialFactorial;
    return factorial(n - 1, n * partialFactorial);
}

This change puts the recursive call to factorial in tail position which allows the function to take advantage of PTC. The number of recursive calls and arithmetic operations is the same for both versions.

This next example involves the functions computeSquareRoot(), computePositiveSquareRoot() and iterativeSquareRoot() which are used to calculate the square roots of numbers using Newton’s Iterative method:

"use strict";
function computeSquareRoot(x)
{
    if (!x)
        return "0";

    let imaginaryPart = "";
    if (x < 0) {
        x = -x;
        imaginaryPart = "i";
    }

    return computePositiveSquareRoot(x).toString() + imaginaryPart;
}

function computePositiveSquareRoot(x)
{
    let targetEpsilon = x / 10000000000;
    return iterativeSquareRoot(x, x / 2, targetEpsilon);
}

function iterativeSquareRoot(x, estimate, targetEpsilon)
{
    let newEstimate = ((x / estimate) + estimate) / 2;
    let delta = Math.abs(estimate - newEstimate);

    if (delta <= targetEpsilon)
        return newEstimate;

    return iterativeSquareRoot(x, newEstimate, targetEpsilon);
}

The top function, computeSquareRoot(), determines if the result will be a real or imaginary number and then calls computePositiveSquareRoot(), which sets up the iterative square process and returns the result of iterativeSquareRoot(). The call to computePositiveSquareRoot() in computeSquareRoot() is not in tail position since additional processing is done on the result of the call. All other function calls are tail position.

Suppose computeSquareRoot() is called with 99 as the argument. It will call computePositiveSquareRoot(99), which will subsequently call iterativeSquareRoot(99, ...). Using Web Inspector, we observed that iterativeSquareRoot() calls back to itself 6 times before returning a result. That result is returned directly back to computeSquareRoot, where it is converted to a string, saving the processing of 7 returns.

This last example shows the type of functional programming that PTC enables:

"use strict";
function newList(count)
{
    let head = { value: count, next: null };
    while (--count)
        head = { value: count, next: head };
    return head;
}

let count = 100000;
let list = newList(count);

function contains(list, x)
{
    if (!list)
        return false;
    if (list.value == x)
        return true;
    return contains(list.next, x);
}
...

if (contains(list, someValue))
   ...

The function contains() searches the list using tail recursion. For a list the size of 100,000 elements given in this example, most browsers will run out of stack memory and throw an exception. In strict mode, where contains() can take advantage of PTC, the program runs just fine. It is also interesting to note that even with a list size small enough to allow this code to run without PTC, using PTC results in the code running 2.5x faster.

Things to Note

There are a couple subtle, but minor issues to be aware of when using PTC. Remember that PTC is only available in strict mode and only for calls made from tail position. The other notable change involves the generation of stack traces. There are some non-standard ECMAScript features in JavaScript that work differently in the presence of PTC. These include Error.stack and the Console object’s stack trace. For example, say a tail called function gonnaThrowAnError() throws an Error object; the function that catches that Error will not see the function that called gonnaThrowAnError() in the Error object’s stack trace. As a general rule, the Console object’s stack trace will not include a function that made a tail call to another function. We call such frames tail deleted frames because its as if they are deleted from the stack when making a call.

Debugging PTC with ShadowChicken

Because PTC places a strict resource guarantee on stack usage, JavaScriptCore cannot keep around information for all tail deleted frames. Keeping around any extra resources, no matter how small, for an unbounded number of tail deleted frames is not possible because it could use an unbounded amount of memory and eventually exhaust the memory limits of the program. Given that tail calls do not keep around any information in the program’s executing state, debugging tail calls can be challenging when using an interactive debugging tool. Without any added machinery, the debugger will not know if a tail call did or did not occur. Because we want to make debugging tail calls inside Web Inspector a great experience, we have implemented mechanisms inside JavaScriptCore to keep around a shadow stack that will display a finite number, currently 128, tail deleted frames. This allows us to both provide strict guarantees on memory usage and to provide users of PTC the benefit of seeing the most important stack frames in their program inside Web Inspector.

We call our shadow stack implementation ShadowChicken. The name is an homage to the CHICKEN scheme interpreter. ShadowChicken uses a logging mechanism for constructing its shadow stack. The log works as follows:

  • On entry to a function, the function’s callee will log a prologue packet.
  • When making a tail call, the caller will log a tail packet just before the tail call occurs.
  • When an exception is thrown, ShadowChicken will log a throw packet.

To construct the shadow stack, ShadowChicken takes two inputs:

  1. The log filled with a sequence of prologue, tail, and throw packets.
  2. The current top of the machine stack (note that the machine stack does not contain any frames that are tail deleted).

Given these two inputs, ShadowChicken is able to construct a shadow stack that includes tail-deleted frames. It will reconcile the machine stack with its log. Because the log has tail packets for when tail calls occurred, it is able to use the log to insert tail-deleted stack frames into the shadow stack to represent frames that were only present on the machine stack before a tail call occurred. ShadowChicken uses a constant amount of memory on top of the memory your program already uses. The log is fixed in size. Whenever ShadowChicken runs out of space in the log, it will perform its reconciliation algorithm to update its internal data structure about the state of the shadow stack. The shadow stack will contain at most 128 tail deleted frames, a number we believe strikes a good balance between programs that intentionally take advantage of PTC and programs that just happen to use PTC without the explicit intention of doing so.

Hooking Up ShadowChicken to Web Inspector

Because JavaScriptCore has the machinery for constructing a shadow stack, Web Inspector can use JavaScriptCore’s shadow stack in its debugger. This allows users of Web Inspector to interact with tail deleted stack frames as if they are real stack frames that are on the current machine stack.

Let’s see some interactions with Web Inspector and the iterative square root code to compute the square root of 99. After setting a breakpoint in iterativeSquareRoot() and invoking computeSquareRoot(99), Web Inspector shows that we are paused, ready to return the result.

Breakpoint with tail deleted frames in the stack.

Web Inspector not only shows the frame we’re stopped in and the original caller, computeSquareRoot(), but also shows the seven tail deleted frames. These are highlighted in the above image. Tail deleted frames in Web Inspector show up with a gray ? icon to their left. As the next image shows, the variables in tail deleted frames can be examined as if the frame were a normal frame. The next image shows Web Inspector inspecting a tail deleted frame one level up from the leaf frame.

Selecting a tail deleted frame.

In this image, the local variables (circled) can be examined. The highlighted line in the program also shows where the tail deleted frame made a tail call from.

The next image shows what happens when single stepping from the breakpoint.

Returning from tail deleted frames.

With on click of the step into button, Web Inspector now shows that we have returned directly to where computeSquareRoot() made the first tail call.

Summary

WebKit has ECMAScript 6 Proper Tail Calls and web developers are encouraged to take advantage of them to design programs in ways that were not possible before. Existing code can benefit as well. Web Inspector makes developing and debugging with PTC straightforward and intuitive.

The current Safari Technology Preview Release 4 has support for PTC. However, Web Inspector was only recently hooked up to work with ShadowChicken and therefore it is not in Safari Technology Preview Release 4. It is expected to be in the next Safari Technology Preview release. You can also try out ShadowChicken in Web Inspector by using a WebKit Nightly build.

Acknowledgements

This article was cowritten with Saam Barati from the JavaScriptCore team. We invite comments and feedback on WebKit’s PTC implementation. Feel free to get in touch with @msaboff, @saambarati, and @jonathandavis on Twitter.

腿疼去医院挂什么科 外露什么意思 切糕为什么这么贵 吃饭快了有什么坏处 bk病毒是什么
为什么会心衰 舌尖有裂纹是什么原因 什么平什么静 迪丽热巴是什么族 电解质水有什么好处
赊事勿取是什么意思 特效是什么意思 龙傲天是什么意思 山西有什么特产 虎什么龙什么
吃什么食物可以降低尿酸 意志是什么意思 rts是什么意思 医保卡是什么样子的图 什么是肾阴虚
床上为什么会有跳蚤hcv8jop2ns8r.cn 胆结石挂什么科室hcv9jop4ns5r.cn 比心什么意思hcv9jop5ns3r.cn 头发油是什么原因hcv9jop4ns7r.cn 坐卧针毡是什么生肖creativexi.com
1987年属什么今年多大hcv7jop4ns6r.cn 王秋儿和王冬儿什么关系hcv8jop7ns1r.cn 肌张力高吃什么药imcecn.com 义眼是什么hcv8jop9ns3r.cn 每天喝奶茶有什么危害hcv8jop4ns7r.cn
纷呈是什么意思hcv8jop0ns6r.cn 不来例假也没怀孕是什么原因gangsutong.com 尿液有泡沫什么原因hcv8jop9ns0r.cn 嘴唇周围长痘痘是什么原因导致wuhaiwuya.com 沙漠玫瑰什么时候开花hcv7jop9ns0r.cn
十二月十号是什么星座hcv8jop7ns5r.cn 血氧饱和度低于90有什么危害kuyehao.com 辛五行属什么hcv8jop2ns7r.cn 毛孔粗大做什么医美hcv8jop5ns9r.cn 胰岛素针头4mm和5mm有什么区别hcv7jop9ns9r.cn
百度