正在显示
55 个修改的文件
包含
4280 行增加
和
3 行删除
| @@ -28,9 +28,9 @@ var tokenUtil = require('./util/tokenUtil'); | @@ -28,9 +28,9 @@ var tokenUtil = require('./util/tokenUtil'); | ||
| 28 | const _ = require('lodash'); | 28 | const _ = require('lodash'); |
| 29 | var status = require('./util/resTemplate') | 29 | var status = require('./util/resTemplate') |
| 30 | 30 | ||
| 31 | + | ||
| 31 | // error handler | 32 | // error handler |
| 32 | onerror(app) | 33 | onerror(app) |
| 33 | - | ||
| 34 | app.use(koaBody({ multipart: true })); | 34 | app.use(koaBody({ multipart: true })); |
| 35 | 35 | ||
| 36 | // middlewares | 36 | // middlewares |
| @@ -50,7 +50,10 @@ app.use(views(__dirname + '/views', { | @@ -50,7 +50,10 @@ app.use(views(__dirname + '/views', { | ||
| 50 | app.use(async (ctx, next) => { | 50 | app.use(async (ctx, next) => { |
| 51 | try{ | 51 | try{ |
| 52 | const start = new Date(); | 52 | const start = new Date(); |
| 53 | - if(filterUrl.indexOf(ctx.request.url) != -1){ | 53 | + const url = ctx.request.url.split('?'); |
| 54 | + console.log(url); | ||
| 55 | + if(filterUrl.indexOf(url[0]) != -1){ | ||
| 56 | + | ||
| 54 | await next(); | 57 | await next(); |
| 55 | }else if(!ctx.header.token){ | 58 | }else if(!ctx.header.token){ |
| 56 | status.catchError(ctx,400,'请登录'); | 59 | status.catchError(ctx,400,'请登录'); |
img.png
已删除
100644 → 0
32 字节
| 1 | +Wed Sep 06 2017 17:57:14 GMT+0800 (CST) |
| 1 | +# download-file | ||
| 2 | + | ||
| 3 | +## Install | ||
| 4 | + | ||
| 5 | +```shell | ||
| 6 | +npm install download-file --save | ||
| 7 | +``` | ||
| 8 | + | ||
| 9 | +## Usage | ||
| 10 | + | ||
| 11 | +```js | ||
| 12 | +var download = require('download-file') | ||
| 13 | + | ||
| 14 | +var url = "http://i.imgur.com/G9bDaPH.jpg" | ||
| 15 | + | ||
| 16 | +var options = { | ||
| 17 | + directory: "./images/cats/", | ||
| 18 | + filename: "cat.gif" | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +download(url, options, function(err){ | ||
| 22 | + if (err) throw err | ||
| 23 | + console.log("meow") | ||
| 24 | +}) | ||
| 25 | +``` | ||
| 26 | + | ||
| 27 | +## API | ||
| 28 | + | ||
| 29 | +### download(url, [options], callback(err)) | ||
| 30 | + | ||
| 31 | +- __url__ string of the file URL to download | ||
| 32 | + | ||
| 33 | +- __options__ object with options | ||
| 34 | + | ||
| 35 | + - __directory__ string with path to directory where to save files (default: current working directory) | ||
| 36 | + - __filename__ string for the name of the file to be saved as (default: filename in the url) | ||
| 37 | + - __timeout__ integer of how long in ms to wait while downloading (default: 20000) | ||
| 38 | + | ||
| 39 | +- __callback__ function to run after |
844.8 KB
| 1 | +var fs = require('fs') | ||
| 2 | +var url = require('url') | ||
| 3 | +var http = require('http') | ||
| 4 | +var https = require('https') | ||
| 5 | +var mkdirp = require('mkdirp') | ||
| 6 | + | ||
| 7 | +module.exports = function download(file, options, callback) { | ||
| 8 | + if (!file) throw("Need a file url to download") | ||
| 9 | + | ||
| 10 | + if (!callback && typeof options === 'function') { | ||
| 11 | + callback = options | ||
| 12 | + } | ||
| 13 | + | ||
| 14 | + options = typeof options === 'object' ? options : {} | ||
| 15 | + options.timeout = options.timeout || 20000 | ||
| 16 | + options.directory = options.directory ? options.directory : '.' | ||
| 17 | + | ||
| 18 | + var uri = file.split('/') | ||
| 19 | + options.filename = options.filename || uri[uri.length - 1] | ||
| 20 | + | ||
| 21 | + var path = options.directory + "/" + options.filename | ||
| 22 | + | ||
| 23 | + if (url.parse(file).protocol === null) { | ||
| 24 | + file = 'http://' + file | ||
| 25 | + req = http | ||
| 26 | + } else if (url.parse(file).protocol === 'https:') { | ||
| 27 | + req = https | ||
| 28 | + } else { | ||
| 29 | + req = http | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + var request = req.get(file, function(response) { | ||
| 33 | + | ||
| 34 | + if (response.statusCode === 200) { | ||
| 35 | + | ||
| 36 | + mkdirp(options.directory, function(err) { | ||
| 37 | + if (err) throw err | ||
| 38 | + var file = fs.createWriteStream(path) | ||
| 39 | + response.pipe(file) | ||
| 40 | + }) | ||
| 41 | + | ||
| 42 | + } else { | ||
| 43 | + | ||
| 44 | + if (callback) callback(response.statusCode) | ||
| 45 | + | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + response.on("end", function(){ | ||
| 49 | + if (callback) callback(false, path) | ||
| 50 | + }) | ||
| 51 | + | ||
| 52 | + request.setTimeout(options.timeout, function () { | ||
| 53 | + request.abort() | ||
| 54 | + callback("Timeout") | ||
| 55 | + }) | ||
| 56 | + | ||
| 57 | + }).on('error', function(e) { | ||
| 58 | + | ||
| 59 | + if (callback) callback(e) | ||
| 60 | + | ||
| 61 | + }) | ||
| 62 | + | ||
| 63 | +} |
| 1 | +../../../_mkdirp@0.5.1@mkdirp/bin/cmd.js |
| 1 | +../../_mkdirp@0.5.1@mkdirp |
| 1 | +{ | ||
| 2 | + "name": "download-file", | ||
| 3 | + "version": "0.1.5", | ||
| 4 | + "description": "Generic file download utility", | ||
| 5 | + "main": "index.js", | ||
| 6 | + "scripts": { | ||
| 7 | + "test": "node test.js" | ||
| 8 | + }, | ||
| 9 | + "author": "Montana Flynn", | ||
| 10 | + "license": "ISC", | ||
| 11 | + "dependencies": { | ||
| 12 | + "mkdirp": "^0.5.0" | ||
| 13 | + }, | ||
| 14 | + "devDependencies": {}, | ||
| 15 | + "keywords": [ | ||
| 16 | + "download", | ||
| 17 | + "file", | ||
| 18 | + "url", | ||
| 19 | + "get", | ||
| 20 | + "http", | ||
| 21 | + "https" | ||
| 22 | + ], | ||
| 23 | + "_from": "download-file@0.1.5", | ||
| 24 | + "_resolved": "http://registry.npm.taobao.org/download-file/download/download-file-0.1.5.tgz" | ||
| 25 | +} |
| 1 | +var dl = require('./index.js') | ||
| 2 | + | ||
| 3 | +var url = "i.imgur.com/G9bDaPH.jpg" | ||
| 4 | + | ||
| 5 | +var opts = { | ||
| 6 | + directory: "./images/cats/", | ||
| 7 | + filename: "cat.gif" | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | +dl(url, opts, function(err){ | ||
| 11 | + if (err) hiss(err) | ||
| 12 | + meow("no protocol") | ||
| 13 | +}) | ||
| 14 | + | ||
| 15 | +var url = "http://i.imgur.com/G9bDaPH.jpg" | ||
| 16 | + | ||
| 17 | +var opts = { | ||
| 18 | + directory: "./images/cats/", | ||
| 19 | + filename: "cat.gif" | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +dl(url, opts, function(err){ | ||
| 23 | + if (err) hiss(err) | ||
| 24 | + meow("http protocol") | ||
| 25 | +}) | ||
| 26 | + | ||
| 27 | +var url = "http://i.imgur.com/G9bDaPH.jpg" | ||
| 28 | + | ||
| 29 | +var opts = { | ||
| 30 | + directory: "./images/cats/", | ||
| 31 | + filename: "cat.gif" | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +dl(url, opts, function(err){ | ||
| 35 | + if (err) hiss(err) | ||
| 36 | + meow("https protocol") | ||
| 37 | +}) | ||
| 38 | + | ||
| 39 | +function meow(msg) { | ||
| 40 | + console.log("\033[32m", "meow: " + msg, "\033[91m") | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +function hiss(err) { | ||
| 44 | + console.log("\033[31m", "hiss", "\033[91m") | ||
| 45 | + throw err | ||
| 46 | +} |
| 1 | +Wed Sep 06 2017 17:57:15 GMT+0800 (CST) |
| 1 | +sudo: false | ||
| 2 | + | ||
| 3 | +language: node_js | ||
| 4 | + | ||
| 5 | +node_js: | ||
| 6 | + - "4" | ||
| 7 | + - "6" | ||
| 8 | + | ||
| 9 | +cache: | ||
| 10 | + directories: | ||
| 11 | + - node_modules | ||
| 12 | + | ||
| 13 | +install: | ||
| 14 | + - npm install | ||
| 15 | + | ||
| 16 | +script: | ||
| 17 | + - npm run test | ||
| 18 | + | ||
| 19 | +# Necessary to compile native modules for io.js v3 or Node.js v4 | ||
| 20 | +env: | ||
| 21 | + - CXX=g++-4.8 | ||
| 22 | + | ||
| 23 | +# Necessary to compile native modules for io.js v3 or Node.js v4 | ||
| 24 | +addons: | ||
| 25 | + apt: | ||
| 26 | + sources: | ||
| 27 | + - ubuntu-toolchain-r-test | ||
| 28 | + packages: | ||
| 29 | + - g++-4.8 | ||
| 30 | + | ||
| 31 | +notifications: | ||
| 32 | + email: false |
node_modules/_fibers@1.0.15@fibers/LICENSE
0 → 100644
| 1 | +Copyright 2011 Marcel Laverdet | ||
| 2 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 3 | +of this software and associated documentation files (the "Software"), to | ||
| 4 | +deal in the Software without restriction, including without limitation the | ||
| 5 | +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| 6 | +sell copies of the Software, and to permit persons to whom the Software is | ||
| 7 | +furnished to do so, subject to the following conditions: | ||
| 8 | + | ||
| 9 | +The above copyright notice and this permission notice shall be included in | ||
| 10 | +all copies or substantial portions of the Software. | ||
| 11 | + | ||
| 12 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 13 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 14 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 15 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 16 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| 17 | +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
| 18 | +IN THE SOFTWARE. |
node_modules/_fibers@1.0.15@fibers/README.md
0 → 100644
| 1 | +fibers(1) -- Fiber support for v8 and Node | ||
| 2 | +========================================== | ||
| 3 | + | ||
| 4 | +INSTALLING | ||
| 5 | +---------- | ||
| 6 | + | ||
| 7 | +### via npm | ||
| 8 | +* `npm install fibers` | ||
| 9 | +* You're done! (see "supported platforms" below if you run into errors) | ||
| 10 | + | ||
| 11 | +### from source | ||
| 12 | +* `git clone git://github.com/laverdet/node-fibers.git` | ||
| 13 | +* `cd node-fibers` | ||
| 14 | +* `npm install` | ||
| 15 | + | ||
| 16 | +Note: node-fibers uses [node-gyp](https://github.com/TooTallNate/node-gyp) for | ||
| 17 | +building. To manually invoke the build process, you can use `node-gyp rebuild`. | ||
| 18 | +This will put the compiled extension in `build/Release/fibers.node`. However, | ||
| 19 | +when you do `require('fibers')`, it will expect the module to be in, for | ||
| 20 | +example, `bin/linux-x64-v8-3.11/fibers.node`. You can manually put the module | ||
| 21 | +here every time you build, or you can use the included build script. Either | ||
| 22 | +`npm install` or `node build -f` will do this for you. If you are going to be | ||
| 23 | +hacking on node-fibers, it may be worthwhile to first do `node-gyp configure` | ||
| 24 | +and then for subsequent rebuilds you can just do `node-gyp build` which will | ||
| 25 | +be faster than a full `npm install` or `node-gyp rebuild`. | ||
| 26 | + | ||
| 27 | +### meteor users please read this | ||
| 28 | +If you're trying to get meteor running and you ended up at this page you're | ||
| 29 | +probably doing something wrong. Please uninstall all versions of NodeJS and | ||
| 30 | +Meteor, then start over. See | ||
| 31 | +[meteor#5124](https://github.com/meteor/meteor/issues/5124) for more | ||
| 32 | +information. | ||
| 33 | + | ||
| 34 | +### supported platforms | ||
| 35 | +If you are running NodeJS version 4.x, 5.x, or 6.x on Linux, OS X, or Windows | ||
| 36 | +(7 or later) then you should be able to install fibers from npm just fine. If | ||
| 37 | +you are running an older (or newer) version of node or some other operating | ||
| 38 | +system you will have to compile fibers on your system. | ||
| 39 | + | ||
| 40 | +(special thanks to [Jeroen Janssen](https://github.com/japj) for his work on | ||
| 41 | +fibers in Windows) | ||
| 42 | + | ||
| 43 | +If you do end up needing to compile fibers first make sure you have node-gyp | ||
| 44 | +installed as a global dependency (`npm install -g node-gyp`), and that you have | ||
| 45 | +setup your build enviroment by following the instructions at | ||
| 46 | +[node-gyp](https://github.com/TooTallNate/node-gyp). Ubuntu-flavored Linux users | ||
| 47 | +may need to run `sudo apt-get install g++` as well. | ||
| 48 | + | ||
| 49 | + | ||
| 50 | +EXAMPLES | ||
| 51 | +-------- | ||
| 52 | + | ||
| 53 | +The examples below describe basic use of `Fiber`, but note that it is **not | ||
| 54 | +recommended** to use `Fiber` without an abstraction in between your code and | ||
| 55 | +fibers. See "FUTURES" below for additional information. | ||
| 56 | + | ||
| 57 | +### Sleep | ||
| 58 | +This is a quick example of how you can write sleep() with fibers. Note that | ||
| 59 | +while the sleep() call is blocking inside the fiber, node is able to handle | ||
| 60 | +other events. | ||
| 61 | + | ||
| 62 | + $ cat sleep.js | ||
| 63 | + | ||
| 64 | +```javascript | ||
| 65 | +var Fiber = require('fibers'); | ||
| 66 | + | ||
| 67 | +function sleep(ms) { | ||
| 68 | + var fiber = Fiber.current; | ||
| 69 | + setTimeout(function() { | ||
| 70 | + fiber.run(); | ||
| 71 | + }, ms); | ||
| 72 | + Fiber.yield(); | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +Fiber(function() { | ||
| 76 | + console.log('wait... ' + new Date); | ||
| 77 | + sleep(1000); | ||
| 78 | + console.log('ok... ' + new Date); | ||
| 79 | +}).run(); | ||
| 80 | +console.log('back in main'); | ||
| 81 | +``` | ||
| 82 | + | ||
| 83 | + $ node sleep.js | ||
| 84 | + wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST) | ||
| 85 | + back in main | ||
| 86 | + ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST) | ||
| 87 | + | ||
| 88 | + | ||
| 89 | +### Incremental Generator | ||
| 90 | +Yielding execution will resume back in the fiber right where you left off. You | ||
| 91 | +can also pass values back and forth through yield() and run(). Again, the node | ||
| 92 | +event loop is never blocked while this script is running. | ||
| 93 | + | ||
| 94 | + $ cat generator.js | ||
| 95 | + | ||
| 96 | +```javascript | ||
| 97 | +var Fiber = require('fibers'); | ||
| 98 | + | ||
| 99 | +var inc = Fiber(function(start) { | ||
| 100 | + var total = start; | ||
| 101 | + while (true) { | ||
| 102 | + total += Fiber.yield(total); | ||
| 103 | + } | ||
| 104 | +}); | ||
| 105 | + | ||
| 106 | +for (var ii = inc.run(1); ii <= 10; ii = inc.run(1)) { | ||
| 107 | + console.log(ii); | ||
| 108 | +} | ||
| 109 | +``` | ||
| 110 | + | ||
| 111 | + $ node generator.js | ||
| 112 | + 1 | ||
| 113 | + 2 | ||
| 114 | + 3 | ||
| 115 | + 4 | ||
| 116 | + 5 | ||
| 117 | + 6 | ||
| 118 | + 7 | ||
| 119 | + 8 | ||
| 120 | + 9 | ||
| 121 | + 10 | ||
| 122 | + | ||
| 123 | + | ||
| 124 | +### Fibonacci Generator | ||
| 125 | +Expanding on the incremental generator above, we can create a generator which | ||
| 126 | +returns a new Fibonacci number with each invocation. You can compare this with | ||
| 127 | +the [ECMAScript Harmony | ||
| 128 | +Generator](http://wiki.ecmascript.org/doku.php?id=harmony:generators) Fibonacci | ||
| 129 | +example. | ||
| 130 | + | ||
| 131 | + $ cat fibonacci.js | ||
| 132 | + | ||
| 133 | +```javascript | ||
| 134 | +var Fiber = require('fibers'); | ||
| 135 | + | ||
| 136 | +// Generator function. Returns a function which returns incrementing | ||
| 137 | +// Fibonacci numbers with each call. | ||
| 138 | +function Fibonacci() { | ||
| 139 | + // Create a new fiber which yields sequential Fibonacci numbers | ||
| 140 | + var fiber = Fiber(function() { | ||
| 141 | + Fiber.yield(0); // F(0) -> 0 | ||
| 142 | + var prev = 0, curr = 1; | ||
| 143 | + while (true) { | ||
| 144 | + Fiber.yield(curr); | ||
| 145 | + var tmp = prev + curr; | ||
| 146 | + prev = curr; | ||
| 147 | + curr = tmp; | ||
| 148 | + } | ||
| 149 | + }); | ||
| 150 | + // Return a bound handle to `run` on this fiber | ||
| 151 | + return fiber.run.bind(fiber); | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +// Initialize a new Fibonacci sequence and iterate up to 1597 | ||
| 155 | +var seq = Fibonacci(); | ||
| 156 | +for (var ii = seq(); ii <= 1597; ii = seq()) { | ||
| 157 | + console.log(ii); | ||
| 158 | +} | ||
| 159 | +``` | ||
| 160 | + | ||
| 161 | + $ node fibonacci.js | ||
| 162 | + 0 | ||
| 163 | + 1 | ||
| 164 | + 1 | ||
| 165 | + 2 | ||
| 166 | + 3 | ||
| 167 | + 5 | ||
| 168 | + 8 | ||
| 169 | + 13 | ||
| 170 | + 21 | ||
| 171 | + 34 | ||
| 172 | + 55 | ||
| 173 | + 89 | ||
| 174 | + 144 | ||
| 175 | + 233 | ||
| 176 | + 377 | ||
| 177 | + 610 | ||
| 178 | + 987 | ||
| 179 | + 1597 | ||
| 180 | + | ||
| 181 | + | ||
| 182 | +### Basic Exceptions | ||
| 183 | +Fibers are exception-safe; exceptions will continue travelling through fiber | ||
| 184 | +boundaries: | ||
| 185 | + | ||
| 186 | + $ cat error.js | ||
| 187 | + | ||
| 188 | +```javascript | ||
| 189 | +var Fiber = require('fibers'); | ||
| 190 | + | ||
| 191 | +var fn = Fiber(function() { | ||
| 192 | + console.log('async work here...'); | ||
| 193 | + Fiber.yield(); | ||
| 194 | + console.log('still working...'); | ||
| 195 | + Fiber.yield(); | ||
| 196 | + console.log('just a little bit more...'); | ||
| 197 | + Fiber.yield(); | ||
| 198 | + throw new Error('oh crap!'); | ||
| 199 | +}); | ||
| 200 | + | ||
| 201 | +try { | ||
| 202 | + while (true) { | ||
| 203 | + fn.run(); | ||
| 204 | + } | ||
| 205 | +} catch(e) { | ||
| 206 | + console.log('safely caught that error!'); | ||
| 207 | + console.log(e.stack); | ||
| 208 | +} | ||
| 209 | +console.log('done!'); | ||
| 210 | +``` | ||
| 211 | + | ||
| 212 | + $ node error.js | ||
| 213 | + async work here... | ||
| 214 | + still working... | ||
| 215 | + just a little bit more... | ||
| 216 | + safely caught that error! | ||
| 217 | + Error: oh crap! | ||
| 218 | + at error.js:11:9 | ||
| 219 | + done! | ||
| 220 | + | ||
| 221 | + | ||
| 222 | +FUTURES | ||
| 223 | +------- | ||
| 224 | + | ||
| 225 | +Using the `Fiber` class without an abstraction in between your code and the raw | ||
| 226 | +API is **not recommended**. `Fiber` is meant to implement the smallest amount of | ||
| 227 | +functionality in order make possible many different programming patterns. This | ||
| 228 | +makes the `Fiber` class relatively lousy to work with directly, but extremely | ||
| 229 | +powerful when coupled with a decent abstraction. There is no right answer for | ||
| 230 | +which abstraction is right for you and your project. Included with `node-fibers` | ||
| 231 | +is an implementation of "futures" which is fiber-aware. Usage of this library | ||
| 232 | +is documented below. There are several other externally-maintained options | ||
| 233 | +which can be found on the [wiki](https://github.com/laverdet/node-fibers/wiki). | ||
| 234 | +You **should** feel encouraged to be creative with fibers and build a solution | ||
| 235 | +which works well with your project. For instance, `Future` is not a good | ||
| 236 | +abstraction to use if you want to build a generator function (see Fibonacci | ||
| 237 | +example above). | ||
| 238 | + | ||
| 239 | +Using `Future` to wrap existing node functions. At no point is the node event | ||
| 240 | +loop blocked: | ||
| 241 | + | ||
| 242 | + $ cat ls.js | ||
| 243 | + | ||
| 244 | +```javascript | ||
| 245 | +var Future = require('fibers/future'); | ||
| 246 | +var fs = Future.wrap(require('fs')); | ||
| 247 | + | ||
| 248 | +Future.task(function() { | ||
| 249 | + // Get a list of files in the directory | ||
| 250 | + var fileNames = fs.readdirFuture('.').wait(); | ||
| 251 | + console.log('Found '+ fileNames.length+ ' files'); | ||
| 252 | + | ||
| 253 | + // Stat each file | ||
| 254 | + var stats = []; | ||
| 255 | + for (var ii = 0; ii < fileNames.length; ++ii) { | ||
| 256 | + stats.push(fs.statFuture(fileNames[ii])); | ||
| 257 | + } | ||
| 258 | + stats.map(function(f) { | ||
| 259 | + f.wait() | ||
| 260 | + }); | ||
| 261 | + | ||
| 262 | + // Print file size | ||
| 263 | + for (var ii = 0; ii < fileNames.length; ++ii) { | ||
| 264 | + console.log(fileNames[ii]+ ': '+ stats[ii].get().size); | ||
| 265 | + } | ||
| 266 | +}).detach(); | ||
| 267 | +``` | ||
| 268 | + | ||
| 269 | + $ node ls.js | ||
| 270 | + Found 11 files | ||
| 271 | + bin: 4096 | ||
| 272 | + fibers.js: 1708 | ||
| 273 | + .gitignore: 37 | ||
| 274 | + README.md: 8664 | ||
| 275 | + future.js: 5833 | ||
| 276 | + .git: 4096 | ||
| 277 | + LICENSE: 1054 | ||
| 278 | + src: 4096 | ||
| 279 | + ls.js: 860 | ||
| 280 | + Makefile: 436 | ||
| 281 | + package.json: 684 | ||
| 282 | + | ||
| 283 | + | ||
| 284 | +The future API is designed to make it easy to move between classic | ||
| 285 | +callback-style code and fiber-aware waiting code: | ||
| 286 | + | ||
| 287 | + $ cat sleep.js | ||
| 288 | + | ||
| 289 | +```javascript | ||
| 290 | +var Future = require('fibers/future'), wait = Future.wait; | ||
| 291 | + | ||
| 292 | +// This function returns a future which resolves after a timeout. This | ||
| 293 | +// demonstrates manually resolving futures. | ||
| 294 | +function sleep(ms) { | ||
| 295 | + var future = new Future; | ||
| 296 | + setTimeout(function() { | ||
| 297 | + future.return(); | ||
| 298 | + }, ms); | ||
| 299 | + return future; | ||
| 300 | +} | ||
| 301 | + | ||
| 302 | +// You can create functions which automatically run in their own fiber and | ||
| 303 | +// return futures that resolve when the fiber returns (this probably sounds | ||
| 304 | +// confusing.. just play with it to understand). | ||
| 305 | +var calcTimerDelta = function(ms) { | ||
| 306 | + var start = new Date; | ||
| 307 | + sleep(ms).wait(); | ||
| 308 | + return new Date - start; | ||
| 309 | +}.future(); // <-- important! | ||
| 310 | + | ||
| 311 | +// And futures also include node-friendly callbacks if you don't want to use | ||
| 312 | +// wait() | ||
| 313 | +calcTimerDelta(2000).resolve(function(err, val) { | ||
| 314 | + console.log('Set timer for 2000ms, waited '+ val+ 'ms'); | ||
| 315 | +}); | ||
| 316 | +``` | ||
| 317 | + | ||
| 318 | + $ node sleep.js | ||
| 319 | + Set timer for 2000ms, waited 2009ms | ||
| 320 | + | ||
| 321 | + | ||
| 322 | +API DOCUMENTATION | ||
| 323 | +----------------- | ||
| 324 | +Fiber's definition looks something like this: | ||
| 325 | + | ||
| 326 | +```javascript | ||
| 327 | +/** | ||
| 328 | + * Instantiate a new Fiber. You may invoke this either as a function or as | ||
| 329 | + * a constructor; the behavior is the same. | ||
| 330 | + * | ||
| 331 | + * When run() is called on this fiber for the first time, `fn` will be | ||
| 332 | + * invoked as the first frame on a new stack. Execution will continue on | ||
| 333 | + * this new stack until `fn` returns, or Fiber.yield() is called. | ||
| 334 | + * | ||
| 335 | + * After the function returns the fiber is reset to original state and | ||
| 336 | + * may be restarted with another call to run(). | ||
| 337 | + */ | ||
| 338 | +function Fiber(fn) { | ||
| 339 | + [native code] | ||
| 340 | +} | ||
| 341 | + | ||
| 342 | +/** | ||
| 343 | + * `Fiber.current` will contain the currently-running Fiber. It will be | ||
| 344 | + * `undefined` if there is no fiber (i.e. the main stack of execution). | ||
| 345 | + * | ||
| 346 | + * See "Garbage Collection" for more information on responsible use of | ||
| 347 | + * `Fiber.current`. | ||
| 348 | + */ | ||
| 349 | +Fiber.current = undefined; | ||
| 350 | + | ||
| 351 | +/** | ||
| 352 | + * `Fiber.yield()` will halt execution of the current fiber and return control | ||
| 353 | + * back to original caller of run(). If an argument is supplied to yield(), | ||
| 354 | + * run() will return that value. | ||
| 355 | + * | ||
| 356 | + * When run() is called again, yield() will return. | ||
| 357 | + * | ||
| 358 | + * Note that this function is a global to allow for correct garbage | ||
| 359 | + * collection. This results in no loss of functionality because it is only | ||
| 360 | + * valid to yield from the currently running fiber anyway. | ||
| 361 | + * | ||
| 362 | + * Note also that `yield` is a reserved word in Javascript. This is normally | ||
| 363 | + * not an issue, however some code linters may complain. Rest assured that it | ||
| 364 | + * will run fine now and in future versions of Javascript. | ||
| 365 | + */ | ||
| 366 | +Fiber.yield = function(param) { | ||
| 367 | + [native code] | ||
| 368 | +} | ||
| 369 | + | ||
| 370 | +/** | ||
| 371 | + * run() will start execution of this Fiber, or if it is currently yielding, | ||
| 372 | + * it will resume execution. If an argument is supplied, this argument will | ||
| 373 | + * be passed to the fiber, either as the first parameter to the main | ||
| 374 | + * function [if the fiber has not been started] or as the return value of | ||
| 375 | + * yield() [if the fiber is currently yielding]. | ||
| 376 | + * | ||
| 377 | + * This function will return either the parameter passed to yield(), or the | ||
| 378 | + * returned value from the fiber's main function. | ||
| 379 | + */ | ||
| 380 | +Fiber.prototype.run = function(param) { | ||
| 381 | + [native code] | ||
| 382 | +} | ||
| 383 | + | ||
| 384 | +/** | ||
| 385 | + * reset() will terminate a running Fiber and restore it to its original | ||
| 386 | + * state, as if it had returned execution. | ||
| 387 | + * | ||
| 388 | + * This is accomplished by causing yield() to throw an exception, and any | ||
| 389 | + * futher calls to yield() will also throw an exception. This continues | ||
| 390 | + * until the fiber has completely unwound and returns. | ||
| 391 | + * | ||
| 392 | + * If the fiber returns a value it will be returned by reset(). | ||
| 393 | + * | ||
| 394 | + * If the fiber is not running, reset() will have no effect. | ||
| 395 | + */ | ||
| 396 | +Fiber.prototype.reset = function() { | ||
| 397 | + [native code] | ||
| 398 | +} | ||
| 399 | + | ||
| 400 | +/** | ||
| 401 | + * throwInto() will cause a currently yielding fiber's yield() call to | ||
| 402 | + * throw instead of return gracefully. This can be useful for notifying a | ||
| 403 | + * fiber that you are no longer interested in its task, and that it should | ||
| 404 | + * give up. | ||
| 405 | + * | ||
| 406 | + * Note that if the fiber does not handle the exception it will continue to | ||
| 407 | + * bubble up and throwInto() will throw the exception right back at you. | ||
| 408 | + */ | ||
| 409 | +Fiber.prototype.throwInto = function(exception) { | ||
| 410 | + [native code] | ||
| 411 | +} | ||
| 412 | +``` | ||
| 413 | + | ||
| 414 | + | ||
| 415 | +Future's definition looks something like this: | ||
| 416 | + | ||
| 417 | +```javascript | ||
| 418 | +/** | ||
| 419 | + * Returns a future-function which, when run, starts running the target | ||
| 420 | + * function and returns a future for the result. | ||
| 421 | + * | ||
| 422 | + * Example usage: | ||
| 423 | + * var funcy = function(arg) { | ||
| 424 | + * return arg+1; | ||
| 425 | + * }.future(); | ||
| 426 | + * | ||
| 427 | + * funcy(1).wait(); // returns 2 | ||
| 428 | + */ | ||
| 429 | +Function.prototype.future = function() { ... } | ||
| 430 | + | ||
| 431 | +/** | ||
| 432 | + * Future object, instantiated with the new operator. | ||
| 433 | + */ | ||
| 434 | +function Future() {} | ||
| 435 | + | ||
| 436 | +/** | ||
| 437 | + * Wrap a node-style async function to return a future in place of using a callback. | ||
| 438 | + * | ||
| 439 | + * fn - the function or object to wrap | ||
| 440 | + * array - indicates that this callback will return more than 1 argument after `err`. For example, | ||
| 441 | + * `child_process.exec()` returns [err, stdout, stderr] | ||
| 442 | + * suffix - appends a string to every method that was overridden, if you passed an object | ||
| 443 | + * | ||
| 444 | + * Example usage: Future.wrap(asyncFunction)(arg1).wait() | ||
| 445 | + */ | ||
| 446 | +Future.wrap = function(fn, multi, suffix) { ... } | ||
| 447 | + | ||
| 448 | +/** | ||
| 449 | + * Invoke a function that will be run in its own fiber context and return a future to its return | ||
| 450 | + * value. | ||
| 451 | + * | ||
| 452 | + * Example: | ||
| 453 | + * Future.task(function() { | ||
| 454 | + * // You can safely `wait` on stuff here | ||
| 455 | + * }).detach(); | ||
| 456 | + */ | ||
| 457 | +Future.task = function(fn) { ... } | ||
| 458 | + | ||
| 459 | +/** | ||
| 460 | + * Wait on a series of futures and then return. If the futures throw an exception this function | ||
| 461 | + * /won't/ throw it back. You can get the value of the future by calling get() on it directly. If | ||
| 462 | + * you want to wait on a single future you're better off calling future.wait() on the instance. | ||
| 463 | + * | ||
| 464 | + * Example usage: Future.wait(aFuture, anotherFuture) | ||
| 465 | + */ | ||
| 466 | +Future.wait = function(/* ... */) { ... } | ||
| 467 | + | ||
| 468 | +/** | ||
| 469 | + * Return the value of this future. If the future hasn't resolved yet this will throw an error. | ||
| 470 | + */ | ||
| 471 | +Future.prototype.get = function() { ... } | ||
| 472 | + | ||
| 473 | +/** | ||
| 474 | + * Mark this future as returned. All pending callbacks will be invoked immediately. | ||
| 475 | + * | ||
| 476 | + * value - the value to return when get() or wait() is called. | ||
| 477 | + * | ||
| 478 | + * Example usage: aFuture.return(value) | ||
| 479 | + */ | ||
| 480 | +Future.prototype.return = function(value) { ... } | ||
| 481 | + | ||
| 482 | +/** | ||
| 483 | + * Throw from this future as returned. All pending callbacks will be invoked immediately. | ||
| 484 | + * Note that execution will continue normally after running this method, | ||
| 485 | + * so make sure you exit appropriately after running throw() | ||
| 486 | + * | ||
| 487 | + * error - the error to throw when get() or wait() is called. | ||
| 488 | + * | ||
| 489 | + * Example usage: aFuture.throw(new Error("Something borked")) | ||
| 490 | + */ | ||
| 491 | +Future.prototype.throw = function(error) { ... } | ||
| 492 | + | ||
| 493 | +/** | ||
| 494 | + * "detach" this future. Basically this is useful if you want to run a task in a future, you | ||
| 495 | + * aren't interested in its return value, but if it throws you don't want the exception to be | ||
| 496 | + * lost. If this fiber throws, an exception will be thrown to the event loop and node will | ||
| 497 | + * probably fall down. | ||
| 498 | + */ | ||
| 499 | +Future.prototype.detach = function() { ... } | ||
| 500 | + | ||
| 501 | +/** | ||
| 502 | + * Returns whether or not this future has resolved yet. | ||
| 503 | + */ | ||
| 504 | +Future.prototype.isResolved = function() { ... } | ||
| 505 | + | ||
| 506 | +/** | ||
| 507 | + * Returns a node-style function which will mark this future as resolved when called. | ||
| 508 | + * | ||
| 509 | + * Example usage: | ||
| 510 | + * var errback = aFuture.resolver(); | ||
| 511 | + * asyncFunction(arg1, arg2, etc, errback) | ||
| 512 | + * var result = aFuture.wait(); | ||
| 513 | + */ | ||
| 514 | +Future.prototype.resolver = function() { ... } | ||
| 515 | + | ||
| 516 | +/** | ||
| 517 | + * Waits for this future to resolve and then invokes a callback. | ||
| 518 | + * | ||
| 519 | + * If only one argument is passed it is a standard function(err, val){} errback. | ||
| 520 | + * | ||
| 521 | + * If two arguments are passed, the first argument is a future which will be thrown to in the case | ||
| 522 | + * of error, and the second is a function(val){} callback. | ||
| 523 | + */ | ||
| 524 | +Future.prototype.resolve = function(/* errback or future, callback */) { ... } | ||
| 525 | + | ||
| 526 | +/** | ||
| 527 | + * Propogate results to another future. | ||
| 528 | + * | ||
| 529 | + * Example usage: future1.proxy(future2) // future2 gets automatically resolved with however future1 resolves | ||
| 530 | + */ | ||
| 531 | +Future.prototype.proxy = function(future) { ... } | ||
| 532 | + | ||
| 533 | +/** | ||
| 534 | + * Differs from its functional counterpart in that it actually resolves the future. Thus if the | ||
| 535 | + * future threw, future.wait() will throw. | ||
| 536 | + */ | ||
| 537 | +Future.prototype.wait = function() { ... } | ||
| 538 | + | ||
| 539 | +/** | ||
| 540 | + * Support for converting a Future to and from ES6 Promises. | ||
| 541 | + */ | ||
| 542 | +Future.fromPromise = function(promise) { ... } | ||
| 543 | +Future.prototype.promise = function() { ... } | ||
| 544 | +``` | ||
| 545 | + | ||
| 546 | +GARBAGE COLLECTION | ||
| 547 | +------------------ | ||
| 548 | + | ||
| 549 | +If you intend to build generators, iterators, or "lazy lists", you should be | ||
| 550 | +aware that all fibers must eventually unwind. This is implemented by causing | ||
| 551 | +yield() to throw unconditionally when the library is trying to unwind your | ||
| 552 | +fiber-- either because reset() was called, or all handles to the fiber were lost | ||
| 553 | +and v8 wants to delete it. | ||
| 554 | + | ||
| 555 | +Something like this will, at some point, cause an infinite loop in your | ||
| 556 | +application: | ||
| 557 | + | ||
| 558 | +```javascript | ||
| 559 | +var fiber = Fiber(function() { | ||
| 560 | + while (true) { | ||
| 561 | + try { | ||
| 562 | + Fiber.yield(); | ||
| 563 | + } catch(e) {} | ||
| 564 | + } | ||
| 565 | +}); | ||
| 566 | +fiber.run(); | ||
| 567 | +``` | ||
| 568 | + | ||
| 569 | +If you either call reset() on this fiber, or the v8 garbage collector decides it | ||
| 570 | +is no longer in use, the fiber library will attempt to unwind the fiber by | ||
| 571 | +causing all calls to yield() to throw. However, if you catch these exceptions | ||
| 572 | +and continue anyway, an infinite loop will occur. | ||
| 573 | + | ||
| 574 | +There are other garbage collection issues that occur with misuse of fiber | ||
| 575 | +handles. If you grab a handle to a fiber from within itself, you should make | ||
| 576 | +sure that the fiber eventually unwinds. This application will leak memory: | ||
| 577 | + | ||
| 578 | +```javascript | ||
| 579 | +var fiber = Fiber(function() { | ||
| 580 | + var that = Fiber.current; | ||
| 581 | + Fiber.yield(); | ||
| 582 | +} | ||
| 583 | +fiber.run(); | ||
| 584 | +fiber = undefined; | ||
| 585 | +``` | ||
| 586 | + | ||
| 587 | +There is no way to get back into the fiber that was started, however it's | ||
| 588 | +impossible for v8's garbage collector to detect this. With a handle to the fiber | ||
| 589 | +still outstanding, v8 will never garbage collect it and the stack will remain in | ||
| 590 | +memory until the application exits. | ||
| 591 | + | ||
| 592 | +Thus, you should take care when grabbing references to `Fiber.current`. |
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
node_modules/_fibers@1.0.15@fibers/bin/repl
0 → 100755
| 1 | +#!/usr/bin/env node | ||
| 2 | +"use strict"; | ||
| 3 | +var fs = require('fs'); | ||
| 4 | +global.Fiber = require('../fibers'); | ||
| 5 | +global.Future = require('../future'); | ||
| 6 | + | ||
| 7 | +// Start the repl | ||
| 8 | +var vm = require('vm'); | ||
| 9 | +var domain = require('domain'); | ||
| 10 | +var repl = require('repl').start('node> ', null, fiberEval, true, true); | ||
| 11 | +function fiberEval(code, context, file, cb) { | ||
| 12 | + if (/^\([ \r\n\t+]\)$/.test(code)) { | ||
| 13 | + return cb(false, undefined); | ||
| 14 | + } | ||
| 15 | + // Parses? | ||
| 16 | + try { | ||
| 17 | + new Function(code); | ||
| 18 | + } catch (err) { | ||
| 19 | + return cb(err, false); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + // Run in fiber | ||
| 23 | + Future.task(function() { | ||
| 24 | + // Save history | ||
| 25 | + var last; | ||
| 26 | + repl.rli.history = repl.rli.history.slice(0, 50).filter(function(item) { | ||
| 27 | + try { | ||
| 28 | + return item !== last; | ||
| 29 | + } finally { | ||
| 30 | + last = item; | ||
| 31 | + } | ||
| 32 | + }); | ||
| 33 | + fs.writeFile(process.env.HOME+ '/.node-history', JSON.stringify(repl.rli.history), function(){}); | ||
| 34 | + | ||
| 35 | + // Run user code | ||
| 36 | + var d = domain.create(); | ||
| 37 | + d.run(function() { | ||
| 38 | + cb(null, vm.runInThisContext(code, file)); | ||
| 39 | + }); | ||
| 40 | + d.on('error', function(err) { | ||
| 41 | + console.error('\nUnhandled error: '+ err.stack); | ||
| 42 | + }); | ||
| 43 | + }).resolve(cb); | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +// Load history | ||
| 47 | +try { | ||
| 48 | + repl.rli.history = JSON.parse(fs.readFileSync(process.env.HOME+ '/.node-history', 'utf-8')); | ||
| 49 | +} catch (err) {} |
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
| 1 | +{ | ||
| 2 | + 'target_defaults': { | ||
| 3 | + 'default_configuration': 'Release', | ||
| 4 | + 'configurations': { | ||
| 5 | + 'Release': { | ||
| 6 | + 'cflags': [ '-O3' ], | ||
| 7 | + 'xcode_settings': { | ||
| 8 | + 'GCC_OPTIMIZATION_LEVEL': '3', | ||
| 9 | + 'GCC_GENERATE_DEBUGGING_SYMBOLS': 'NO', | ||
| 10 | + }, | ||
| 11 | + 'msvs_settings': { | ||
| 12 | + 'VCCLCompilerTool': { | ||
| 13 | + 'Optimization': 3, | ||
| 14 | + 'FavorSizeOrSpeed': 1, | ||
| 15 | + }, | ||
| 16 | + }, | ||
| 17 | + } | ||
| 18 | + }, | ||
| 19 | + }, | ||
| 20 | + 'targets': [ | ||
| 21 | + { | ||
| 22 | + 'target_name': 'fibers', | ||
| 23 | + 'sources': [ | ||
| 24 | + 'src/fibers.cc', | ||
| 25 | + 'src/coroutine.cc', | ||
| 26 | + 'src/libcoro/coro.c', | ||
| 27 | + # Rebuild on header changes | ||
| 28 | + 'src/coroutine.h', | ||
| 29 | + 'src/libcoro/coro.h', | ||
| 30 | + ], | ||
| 31 | + 'cflags!': ['-ansi'], | ||
| 32 | + 'conditions': [ | ||
| 33 | + ['OS == "win"', | ||
| 34 | + {'defines': ['CORO_FIBER', 'WINDOWS']}, | ||
| 35 | + # else | ||
| 36 | + { | ||
| 37 | + 'defines': ['USE_CORO', 'CORO_GUARDPAGES=1'], | ||
| 38 | + 'ldflags': ['-pthread'], | ||
| 39 | + } | ||
| 40 | + ], | ||
| 41 | + ['OS == "linux"', | ||
| 42 | + { | ||
| 43 | + 'variables': { | ||
| 44 | + 'USE_MUSL': '<!(ldd --version 2>&1 | head -n1 | grep "musl" | wc -l)', | ||
| 45 | + }, | ||
| 46 | + 'conditions': [ | ||
| 47 | + ['<(USE_MUSL) == 1', | ||
| 48 | + {'defines': ['CORO_ASM', 'USE_V8_SYMBOLS']}, | ||
| 49 | + {'defines': ['CORO_UCONTEXT']} | ||
| 50 | + ], | ||
| 51 | + ], | ||
| 52 | + }, | ||
| 53 | + ], | ||
| 54 | + ['OS == "solaris" or OS == "sunos" or OS == "freebsd" or OS == "aix"', {'defines': ['CORO_UCONTEXT']}], | ||
| 55 | + ['OS == "mac"', {'defines': ['CORO_SJLJ']}], | ||
| 56 | + ['OS == "openbsd"', {'defines': ['CORO_ASM']}], | ||
| 57 | + ['target_arch == "arm" or target_arch == "arm64"', | ||
| 58 | + { | ||
| 59 | + # There's been problems getting real fibers working on arm | ||
| 60 | + 'defines': ['CORO_PTHREAD'], | ||
| 61 | + 'defines!': ['CORO_UCONTEXT', 'CORO_SJLJ', 'CORO_ASM'], | ||
| 62 | + }, | ||
| 63 | + ], | ||
| 64 | + ], | ||
| 65 | + }, | ||
| 66 | + ], | ||
| 67 | +} |
node_modules/_fibers@1.0.15@fibers/build.js
0 → 100755
| 1 | +#!/usr/bin/env node | ||
| 2 | +var cp = require('child_process'), | ||
| 3 | + fs = require('fs'), | ||
| 4 | + path = require('path'); | ||
| 5 | + | ||
| 6 | +// Parse args | ||
| 7 | +var force = false, debug = false; | ||
| 8 | +var | ||
| 9 | + arch = process.arch, | ||
| 10 | + platform = process.platform; | ||
| 11 | +var args = process.argv.slice(2).filter(function(arg) { | ||
| 12 | + if (arg === '-f') { | ||
| 13 | + force = true; | ||
| 14 | + return false; | ||
| 15 | + } else if (arg.substring(0, 13) === '--target_arch') { | ||
| 16 | + arch = arg.substring(14); | ||
| 17 | + } else if (arg === '--debug') { | ||
| 18 | + debug = true; | ||
| 19 | + } | ||
| 20 | + return true; | ||
| 21 | +}); | ||
| 22 | +if (!debug) { | ||
| 23 | + args.push('--release'); | ||
| 24 | +} | ||
| 25 | +if (!{ia32: true, x64: true, arm: true, arm64: true, ppc: true, ppc64: true, s390: true, s390x: true}.hasOwnProperty(arch)) { | ||
| 26 | + console.error('Unsupported (?) architecture: `'+ arch+ '`'); | ||
| 27 | + process.exit(1); | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +// Test for pre-built library | ||
| 31 | +var modPath = platform+ '-'+ arch+ '-'+ process.versions.modules; | ||
| 32 | +if (!force) { | ||
| 33 | + try { | ||
| 34 | + fs.statSync(path.join(__dirname, 'bin', modPath, 'fibers.node')); | ||
| 35 | + console.log('`'+ modPath+ '` exists; testing'); | ||
| 36 | + cp.execFile(process.execPath, ['quick-test'], function(err, stdout, stderr) { | ||
| 37 | + if (err || stdout !== 'pass' || stderr) { | ||
| 38 | + console.log('Problem with the binary; manual build incoming'); | ||
| 39 | + build(); | ||
| 40 | + } else { | ||
| 41 | + console.log('Binary is fine; exiting'); | ||
| 42 | + } | ||
| 43 | + }); | ||
| 44 | + } catch (ex) { | ||
| 45 | + // Stat failed | ||
| 46 | + build(); | ||
| 47 | + } | ||
| 48 | +} else { | ||
| 49 | + build(); | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +// Build it | ||
| 53 | +function build() { | ||
| 54 | + if (process.versions.electron) { | ||
| 55 | + args.push('--target='+ process.versions.electron, '--dist-url=https://atom.io/download/atom-shell'); | ||
| 56 | + } | ||
| 57 | + cp.spawn( | ||
| 58 | + process.platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp', | ||
| 59 | + ['rebuild'].concat(args), | ||
| 60 | + {stdio: [process.stdin, process.stdout, process.stderr]}) | ||
| 61 | + .on('exit', function(err) { | ||
| 62 | + if (err) { | ||
| 63 | + console.error( | ||
| 64 | + 'node-gyp exited with code: '+ err+ '\n'+ | ||
| 65 | + 'Please make sure you are using a supported platform and node version. If you\n'+ | ||
| 66 | + 'would like to compile fibers on this machine please make sure you have setup your\n'+ | ||
| 67 | + 'build environment--\n'+ | ||
| 68 | + 'Windows + OS X instructions here: https://github.com/nodejs/node-gyp\n'+ | ||
| 69 | + 'Ubuntu users please run: `sudo apt-get install g++ build-essential`\n'+ | ||
| 70 | + 'Alpine users please run: `sudo apk add python make g++`' | ||
| 71 | + ); | ||
| 72 | + return process.exit(err); | ||
| 73 | + } | ||
| 74 | + afterBuild(); | ||
| 75 | + }) | ||
| 76 | + .on('error', function(err) { | ||
| 77 | + console.error( | ||
| 78 | + 'node-gyp not found! Please ensure node-gyp is in your PATH--\n'+ | ||
| 79 | + 'Try running: `sudo npm install -g node-gyp`' | ||
| 80 | + ); | ||
| 81 | + console.log(err.message); | ||
| 82 | + process.exit(1); | ||
| 83 | + }); | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +// Move it to expected location | ||
| 87 | +function afterBuild() { | ||
| 88 | + var targetPath = path.join(__dirname, 'build', debug ? 'Debug' : 'Release', 'fibers.node'); | ||
| 89 | + var installPath = path.join(__dirname, 'bin', modPath, 'fibers.node'); | ||
| 90 | + | ||
| 91 | + try { | ||
| 92 | + fs.mkdirSync(path.join(__dirname, 'bin', modPath)); | ||
| 93 | + } catch (ex) {} | ||
| 94 | + | ||
| 95 | + try { | ||
| 96 | + fs.statSync(targetPath); | ||
| 97 | + } catch (ex) { | ||
| 98 | + console.error('Build succeeded but target not found'); | ||
| 99 | + process.exit(1); | ||
| 100 | + } | ||
| 101 | + fs.renameSync(targetPath, installPath); | ||
| 102 | + console.log('Installed in `'+ installPath+ '`'); | ||
| 103 | + if (process.versions.electron) { | ||
| 104 | + process.nextTick(function() { | ||
| 105 | + require('electron').app.quit(); | ||
| 106 | + }); | ||
| 107 | + } | ||
| 108 | +} |
| 1 | +# We borrow heavily from the kernel build setup, though we are simpler since | ||
| 2 | +# we don't have Kconfig tweaking settings on us. | ||
| 3 | + | ||
| 4 | +# The implicit make rules have it looking for RCS files, among other things. | ||
| 5 | +# We instead explicitly write all the rules we care about. | ||
| 6 | +# It's even quicker (saves ~200ms) to pass -r on the command line. | ||
| 7 | +MAKEFLAGS=-r | ||
| 8 | + | ||
| 9 | +# The source directory tree. | ||
| 10 | +srcdir := .. | ||
| 11 | +abs_srcdir := $(abspath $(srcdir)) | ||
| 12 | + | ||
| 13 | +# The name of the builddir. | ||
| 14 | +builddir_name ?= . | ||
| 15 | + | ||
| 16 | +# The V=1 flag on command line makes us verbosely print command lines. | ||
| 17 | +ifdef V | ||
| 18 | + quiet= | ||
| 19 | +else | ||
| 20 | + quiet=quiet_ | ||
| 21 | +endif | ||
| 22 | + | ||
| 23 | +# Specify BUILDTYPE=Release on the command line for a release build. | ||
| 24 | +BUILDTYPE ?= Release | ||
| 25 | + | ||
| 26 | +# Directory all our build output goes into. | ||
| 27 | +# Note that this must be two directories beneath src/ for unit tests to pass, | ||
| 28 | +# as they reach into the src/ directory for data with relative paths. | ||
| 29 | +builddir ?= $(builddir_name)/$(BUILDTYPE) | ||
| 30 | +abs_builddir := $(abspath $(builddir)) | ||
| 31 | +depsdir := $(builddir)/.deps | ||
| 32 | + | ||
| 33 | +# Object output directory. | ||
| 34 | +obj := $(builddir)/obj | ||
| 35 | +abs_obj := $(abspath $(obj)) | ||
| 36 | + | ||
| 37 | +# We build up a list of every single one of the targets so we can slurp in the | ||
| 38 | +# generated dependency rule Makefiles in one pass. | ||
| 39 | +all_deps := | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + | ||
| 43 | +CC.target ?= $(CC) | ||
| 44 | +CFLAGS.target ?= $(CPPFLAGS) $(CFLAGS) | ||
| 45 | +CXX.target ?= $(CXX) | ||
| 46 | +CXXFLAGS.target ?= $(CPPFLAGS) $(CXXFLAGS) | ||
| 47 | +LINK.target ?= $(LINK) | ||
| 48 | +LDFLAGS.target ?= $(LDFLAGS) | ||
| 49 | +AR.target ?= $(AR) | ||
| 50 | + | ||
| 51 | +# C++ apps need to be linked with g++. | ||
| 52 | +LINK ?= $(CXX.target) | ||
| 53 | + | ||
| 54 | +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need | ||
| 55 | +# to replicate this environment fallback in make as well. | ||
| 56 | +CC.host ?= gcc | ||
| 57 | +CFLAGS.host ?= $(CPPFLAGS_host) $(CFLAGS_host) | ||
| 58 | +CXX.host ?= g++ | ||
| 59 | +CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host) | ||
| 60 | +LINK.host ?= $(CXX.host) | ||
| 61 | +LDFLAGS.host ?= | ||
| 62 | +AR.host ?= ar | ||
| 63 | + | ||
| 64 | +# Define a dir function that can handle spaces. | ||
| 65 | +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions | ||
| 66 | +# "leading spaces cannot appear in the text of the first argument as written. | ||
| 67 | +# These characters can be put into the argument value by variable substitution." | ||
| 68 | +empty := | ||
| 69 | +space := $(empty) $(empty) | ||
| 70 | + | ||
| 71 | +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces | ||
| 72 | +replace_spaces = $(subst $(space),?,$1) | ||
| 73 | +unreplace_spaces = $(subst ?,$(space),$1) | ||
| 74 | +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) | ||
| 75 | + | ||
| 76 | +# Flags to make gcc output dependency info. Note that you need to be | ||
| 77 | +# careful here to use the flags that ccache and distcc can understand. | ||
| 78 | +# We write to a dep file on the side first and then rename at the end | ||
| 79 | +# so we can't end up with a broken dep file. | ||
| 80 | +depfile = $(depsdir)/$(call replace_spaces,$@).d | ||
| 81 | +DEPFLAGS = -MMD -MF $(depfile).raw | ||
| 82 | + | ||
| 83 | +# We have to fixup the deps output in a few ways. | ||
| 84 | +# (1) the file output should mention the proper .o file. | ||
| 85 | +# ccache or distcc lose the path to the target, so we convert a rule of | ||
| 86 | +# the form: | ||
| 87 | +# foobar.o: DEP1 DEP2 | ||
| 88 | +# into | ||
| 89 | +# path/to/foobar.o: DEP1 DEP2 | ||
| 90 | +# (2) we want missing files not to cause us to fail to build. | ||
| 91 | +# We want to rewrite | ||
| 92 | +# foobar.o: DEP1 DEP2 \ | ||
| 93 | +# DEP3 | ||
| 94 | +# to | ||
| 95 | +# DEP1: | ||
| 96 | +# DEP2: | ||
| 97 | +# DEP3: | ||
| 98 | +# so if the files are missing, they're just considered phony rules. | ||
| 99 | +# We have to do some pretty insane escaping to get those backslashes | ||
| 100 | +# and dollar signs past make, the shell, and sed at the same time. | ||
| 101 | +# Doesn't work with spaces, but that's fine: .d files have spaces in | ||
| 102 | +# their names replaced with other characters. | ||
| 103 | +define fixup_dep | ||
| 104 | +# The depfile may not exist if the input file didn't have any #includes. | ||
| 105 | +touch $(depfile).raw | ||
| 106 | +# Fixup path as in (1). | ||
| 107 | +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) | ||
| 108 | +# Add extra rules as in (2). | ||
| 109 | +# We remove slashes and replace spaces with new lines; | ||
| 110 | +# remove blank lines; | ||
| 111 | +# delete the first line and append a colon to the remaining lines. | ||
| 112 | +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ | ||
| 113 | + grep -v '^$$' |\ | ||
| 114 | + sed -e 1d -e 's|$$|:|' \ | ||
| 115 | + >> $(depfile) | ||
| 116 | +rm $(depfile).raw | ||
| 117 | +endef | ||
| 118 | + | ||
| 119 | +# Command definitions: | ||
| 120 | +# - cmd_foo is the actual command to run; | ||
| 121 | +# - quiet_cmd_foo is the brief-output summary of the command. | ||
| 122 | + | ||
| 123 | +quiet_cmd_cc = CC($(TOOLSET)) $@ | ||
| 124 | +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< | ||
| 125 | + | ||
| 126 | +quiet_cmd_cxx = CXX($(TOOLSET)) $@ | ||
| 127 | +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< | ||
| 128 | + | ||
| 129 | +quiet_cmd_objc = CXX($(TOOLSET)) $@ | ||
| 130 | +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< | ||
| 131 | + | ||
| 132 | +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ | ||
| 133 | +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< | ||
| 134 | + | ||
| 135 | +# Commands for precompiled header files. | ||
| 136 | +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ | ||
| 137 | +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< | ||
| 138 | +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ | ||
| 139 | +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< | ||
| 140 | +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ | ||
| 141 | +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< | ||
| 142 | +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ | ||
| 143 | +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< | ||
| 144 | + | ||
| 145 | +# gyp-mac-tool is written next to the root Makefile by gyp. | ||
| 146 | +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd | ||
| 147 | +# already. | ||
| 148 | +quiet_cmd_mac_tool = MACTOOL $(4) $< | ||
| 149 | +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" | ||
| 150 | + | ||
| 151 | +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ | ||
| 152 | +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) | ||
| 153 | + | ||
| 154 | +quiet_cmd_infoplist = INFOPLIST $@ | ||
| 155 | +cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" | ||
| 156 | + | ||
| 157 | +quiet_cmd_touch = TOUCH $@ | ||
| 158 | +cmd_touch = touch $@ | ||
| 159 | + | ||
| 160 | +quiet_cmd_copy = COPY $@ | ||
| 161 | +# send stderr to /dev/null to ignore messages when linking directories. | ||
| 162 | +cmd_copy = rm -rf "$@" && cp -af "$<" "$@" | ||
| 163 | + | ||
| 164 | +quiet_cmd_alink = LIBTOOL-STATIC $@ | ||
| 165 | +cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %.o,$^) | ||
| 166 | + | ||
| 167 | +quiet_cmd_link = LINK($(TOOLSET)) $@ | ||
| 168 | +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) | ||
| 169 | + | ||
| 170 | +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ | ||
| 171 | +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) | ||
| 172 | + | ||
| 173 | +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ | ||
| 174 | +cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) | ||
| 175 | + | ||
| 176 | + | ||
| 177 | +# Define an escape_quotes function to escape single quotes. | ||
| 178 | +# This allows us to handle quotes properly as long as we always use | ||
| 179 | +# use single quotes and escape_quotes. | ||
| 180 | +escape_quotes = $(subst ','\'',$(1)) | ||
| 181 | +# This comment is here just to include a ' to unconfuse syntax highlighting. | ||
| 182 | +# Define an escape_vars function to escape '$' variable syntax. | ||
| 183 | +# This allows us to read/write command lines with shell variables (e.g. | ||
| 184 | +# $LD_LIBRARY_PATH), without triggering make substitution. | ||
| 185 | +escape_vars = $(subst $$,$$$$,$(1)) | ||
| 186 | +# Helper that expands to a shell command to echo a string exactly as it is in | ||
| 187 | +# make. This uses printf instead of echo because printf's behaviour with respect | ||
| 188 | +# to escape sequences is more portable than echo's across different shells | ||
| 189 | +# (e.g., dash, bash). | ||
| 190 | +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' | ||
| 191 | + | ||
| 192 | +# Helper to compare the command we're about to run against the command | ||
| 193 | +# we logged the last time we ran the command. Produces an empty | ||
| 194 | +# string (false) when the commands match. | ||
| 195 | +# Tricky point: Make has no string-equality test function. | ||
| 196 | +# The kernel uses the following, but it seems like it would have false | ||
| 197 | +# positives, where one string reordered its arguments. | ||
| 198 | +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ | ||
| 199 | +# $(filter-out $(cmd_$@), $(cmd_$(1)))) | ||
| 200 | +# We instead substitute each for the empty string into the other, and | ||
| 201 | +# say they're equal if both substitutions produce the empty string. | ||
| 202 | +# .d files contain ? instead of spaces, take that into account. | ||
| 203 | +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ | ||
| 204 | + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) | ||
| 205 | + | ||
| 206 | +# Helper that is non-empty when a prerequisite changes. | ||
| 207 | +# Normally make does this implicitly, but we force rules to always run | ||
| 208 | +# so we can check their command lines. | ||
| 209 | +# $? -- new prerequisites | ||
| 210 | +# $| -- order-only dependencies | ||
| 211 | +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) | ||
| 212 | + | ||
| 213 | +# Helper that executes all postbuilds until one fails. | ||
| 214 | +define do_postbuilds | ||
| 215 | + @E=0;\ | ||
| 216 | + for p in $(POSTBUILDS); do\ | ||
| 217 | + eval $$p;\ | ||
| 218 | + E=$$?;\ | ||
| 219 | + if [ $$E -ne 0 ]; then\ | ||
| 220 | + break;\ | ||
| 221 | + fi;\ | ||
| 222 | + done;\ | ||
| 223 | + if [ $$E -ne 0 ]; then\ | ||
| 224 | + rm -rf "$@";\ | ||
| 225 | + exit $$E;\ | ||
| 226 | + fi | ||
| 227 | +endef | ||
| 228 | + | ||
| 229 | +# do_cmd: run a command via the above cmd_foo names, if necessary. | ||
| 230 | +# Should always run for a given target to handle command-line changes. | ||
| 231 | +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. | ||
| 232 | +# Third argument, if non-zero, makes it do POSTBUILDS processing. | ||
| 233 | +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for | ||
| 234 | +# spaces already and dirx strips the ? characters. | ||
| 235 | +define do_cmd | ||
| 236 | +$(if $(or $(command_changed),$(prereq_changed)), | ||
| 237 | + @$(call exact_echo, $($(quiet)cmd_$(1))) | ||
| 238 | + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" | ||
| 239 | + $(if $(findstring flock,$(word 2,$(cmd_$1))), | ||
| 240 | + @$(cmd_$(1)) | ||
| 241 | + @echo " $(quiet_cmd_$(1)): Finished", | ||
| 242 | + @$(cmd_$(1)) | ||
| 243 | + ) | ||
| 244 | + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) | ||
| 245 | + @$(if $(2),$(fixup_dep)) | ||
| 246 | + $(if $(and $(3), $(POSTBUILDS)), | ||
| 247 | + $(call do_postbuilds) | ||
| 248 | + ) | ||
| 249 | +) | ||
| 250 | +endef | ||
| 251 | + | ||
| 252 | +# Declare the "all" target first so it is the default, | ||
| 253 | +# even though we don't have the deps yet. | ||
| 254 | +.PHONY: all | ||
| 255 | +all: | ||
| 256 | + | ||
| 257 | +# make looks for ways to re-generate included makefiles, but in our case, we | ||
| 258 | +# don't have a direct way. Explicitly telling make that it has nothing to do | ||
| 259 | +# for them makes it go faster. | ||
| 260 | +%.d: ; | ||
| 261 | + | ||
| 262 | +# Use FORCE_DO_CMD to force a target to run. Should be coupled with | ||
| 263 | +# do_cmd. | ||
| 264 | +.PHONY: FORCE_DO_CMD | ||
| 265 | +FORCE_DO_CMD: | ||
| 266 | + | ||
| 267 | +TOOLSET := target | ||
| 268 | +# Suffix rules, putting all outputs into $(obj). | ||
| 269 | +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD | ||
| 270 | + @$(call do_cmd,cc,1) | ||
| 271 | +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD | ||
| 272 | + @$(call do_cmd,cxx,1) | ||
| 273 | +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD | ||
| 274 | + @$(call do_cmd,cxx,1) | ||
| 275 | +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD | ||
| 276 | + @$(call do_cmd,cxx,1) | ||
| 277 | +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD | ||
| 278 | + @$(call do_cmd,objc,1) | ||
| 279 | +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD | ||
| 280 | + @$(call do_cmd,objcxx,1) | ||
| 281 | +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD | ||
| 282 | + @$(call do_cmd,cc,1) | ||
| 283 | +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD | ||
| 284 | + @$(call do_cmd,cc,1) | ||
| 285 | + | ||
| 286 | +# Try building from generated source, too. | ||
| 287 | +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD | ||
| 288 | + @$(call do_cmd,cc,1) | ||
| 289 | +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD | ||
| 290 | + @$(call do_cmd,cxx,1) | ||
| 291 | +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD | ||
| 292 | + @$(call do_cmd,cxx,1) | ||
| 293 | +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD | ||
| 294 | + @$(call do_cmd,cxx,1) | ||
| 295 | +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD | ||
| 296 | + @$(call do_cmd,objc,1) | ||
| 297 | +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD | ||
| 298 | + @$(call do_cmd,objcxx,1) | ||
| 299 | +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD | ||
| 300 | + @$(call do_cmd,cc,1) | ||
| 301 | +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD | ||
| 302 | + @$(call do_cmd,cc,1) | ||
| 303 | + | ||
| 304 | +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD | ||
| 305 | + @$(call do_cmd,cc,1) | ||
| 306 | +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD | ||
| 307 | + @$(call do_cmd,cxx,1) | ||
| 308 | +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD | ||
| 309 | + @$(call do_cmd,cxx,1) | ||
| 310 | +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD | ||
| 311 | + @$(call do_cmd,cxx,1) | ||
| 312 | +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD | ||
| 313 | + @$(call do_cmd,objc,1) | ||
| 314 | +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD | ||
| 315 | + @$(call do_cmd,objcxx,1) | ||
| 316 | +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD | ||
| 317 | + @$(call do_cmd,cc,1) | ||
| 318 | +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD | ||
| 319 | + @$(call do_cmd,cc,1) | ||
| 320 | + | ||
| 321 | + | ||
| 322 | +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ | ||
| 323 | + $(findstring $(join ^,$(prefix)),\ | ||
| 324 | + $(join ^,fibers.target.mk)))),) | ||
| 325 | + include fibers.target.mk | ||
| 326 | +endif | ||
| 327 | + | ||
| 328 | +quiet_cmd_regen_makefile = ACTION Regenerating $@ | ||
| 329 | +cmd_regen_makefile = cd $(srcdir); /usr/local/lib/node_modules/cnpm/node_modules/npminstall/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "--toplevel-dir=." -I/Users/fzy/project/koa2_Sequelize_project/node_modules/_fibers@1.0.15@fibers/build/config.gypi -I/usr/local/lib/node_modules/cnpm/node_modules/npminstall/node_modules/node-gyp/addon.gypi -I/Users/fzy/.node-gyp/8.2.1/include/node/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/fzy/.node-gyp/8.2.1" "-Dnode_gyp_dir=/usr/local/lib/node_modules/cnpm/node_modules/npminstall/node_modules/node-gyp" "-Dnode_lib_file=node.lib" "-Dmodule_root_dir=/Users/fzy/project/koa2_Sequelize_project/node_modules/_fibers@1.0.15@fibers" "-Dnode_engine=v8" binding.gyp | ||
| 330 | +Makefile: $(srcdir)/../../../../.node-gyp/8.2.1/include/node/common.gypi $(srcdir)/../../../../../../usr/local/lib/node_modules/cnpm/node_modules/npminstall/node_modules/node-gyp/addon.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp | ||
| 331 | + $(call do_cmd,regen_makefile) | ||
| 332 | + | ||
| 333 | +# "all" is a concatenation of the "all" targets from all the included | ||
| 334 | +# sub-makefiles. This is just here to clarify. | ||
| 335 | +all: | ||
| 336 | + | ||
| 337 | +# Add in dependency-tracking rules. $(all_deps) is the list of every single | ||
| 338 | +# target in our tree. Only consider the ones with .d (dependency) info: | ||
| 339 | +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) | ||
| 340 | +ifneq ($(d_files),) | ||
| 341 | + include $(d_files) | ||
| 342 | +endif |
| 1 | +cmd_Release/fibers.node := c++ -bundle -undefined dynamic_lookup -Wl,-no_pie -Wl,-search_paths_first -mmacosx-version-min=10.7 -arch x86_64 -L./Release -stdlib=libc++ -o Release/fibers.node Release/obj.target/fibers/src/fibers.o Release/obj.target/fibers/src/coroutine.o Release/obj.target/fibers/src/libcoro/coro.o |
node_modules/_fibers@1.0.15@fibers/build/Release/.deps/Release/obj.target/fibers/src/coroutine.o.d
0 → 100644
| 1 | +cmd_Release/obj.target/fibers/src/coroutine.o := c++ '-DNODE_GYP_MODULE_NAME=fibers' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DUSE_CORO' '-DCORO_GUARDPAGES=1' '-DCORO_SJLJ' '-DBUILDING_NODE_EXTENSION' -I/Users/fzy/.node-gyp/8.2.1/include/node -I/Users/fzy/.node-gyp/8.2.1/src -I/Users/fzy/.node-gyp/8.2.1/deps/uv/include -I/Users/fzy/.node-gyp/8.2.1/deps/v8/include -O3 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=gnu++0x -stdlib=libc++ -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/fibers/src/coroutine.o.d.raw -c -o Release/obj.target/fibers/src/coroutine.o ../src/coroutine.cc | ||
| 2 | +Release/obj.target/fibers/src/coroutine.o: ../src/coroutine.cc \ | ||
| 3 | + ../src/coroutine.h /Users/fzy/.node-gyp/8.2.1/include/node/node.h \ | ||
| 4 | + /Users/fzy/.node-gyp/8.2.1/include/node/v8.h \ | ||
| 5 | + /Users/fzy/.node-gyp/8.2.1/include/node/v8-version.h \ | ||
| 6 | + /Users/fzy/.node-gyp/8.2.1/include/node/v8config.h \ | ||
| 7 | + /Users/fzy/.node-gyp/8.2.1/include/node/node_version.h \ | ||
| 8 | + ../src/libcoro/coro.h ../src/v8-version.h | ||
| 9 | +../src/coroutine.cc: | ||
| 10 | +../src/coroutine.h: | ||
| 11 | +/Users/fzy/.node-gyp/8.2.1/include/node/node.h: | ||
| 12 | +/Users/fzy/.node-gyp/8.2.1/include/node/v8.h: | ||
| 13 | +/Users/fzy/.node-gyp/8.2.1/include/node/v8-version.h: | ||
| 14 | +/Users/fzy/.node-gyp/8.2.1/include/node/v8config.h: | ||
| 15 | +/Users/fzy/.node-gyp/8.2.1/include/node/node_version.h: | ||
| 16 | +../src/libcoro/coro.h: | ||
| 17 | +../src/v8-version.h: |
node_modules/_fibers@1.0.15@fibers/build/Release/.deps/Release/obj.target/fibers/src/fibers.o.d
0 → 100644
| 1 | +cmd_Release/obj.target/fibers/src/fibers.o := c++ '-DNODE_GYP_MODULE_NAME=fibers' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DUSE_CORO' '-DCORO_GUARDPAGES=1' '-DCORO_SJLJ' '-DBUILDING_NODE_EXTENSION' -I/Users/fzy/.node-gyp/8.2.1/include/node -I/Users/fzy/.node-gyp/8.2.1/src -I/Users/fzy/.node-gyp/8.2.1/deps/uv/include -I/Users/fzy/.node-gyp/8.2.1/deps/v8/include -O3 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=gnu++0x -stdlib=libc++ -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/fibers/src/fibers.o.d.raw -c -o Release/obj.target/fibers/src/fibers.o ../src/fibers.cc | ||
| 2 | +Release/obj.target/fibers/src/fibers.o: ../src/fibers.cc \ | ||
| 3 | + ../src/coroutine.h /Users/fzy/.node-gyp/8.2.1/include/node/node.h \ | ||
| 4 | + /Users/fzy/.node-gyp/8.2.1/include/node/v8.h \ | ||
| 5 | + /Users/fzy/.node-gyp/8.2.1/include/node/v8-version.h \ | ||
| 6 | + /Users/fzy/.node-gyp/8.2.1/include/node/v8config.h \ | ||
| 7 | + /Users/fzy/.node-gyp/8.2.1/include/node/node_version.h \ | ||
| 8 | + ../src/libcoro/coro.h ../src/v8-version.h | ||
| 9 | +../src/fibers.cc: | ||
| 10 | +../src/coroutine.h: | ||
| 11 | +/Users/fzy/.node-gyp/8.2.1/include/node/node.h: | ||
| 12 | +/Users/fzy/.node-gyp/8.2.1/include/node/v8.h: | ||
| 13 | +/Users/fzy/.node-gyp/8.2.1/include/node/v8-version.h: | ||
| 14 | +/Users/fzy/.node-gyp/8.2.1/include/node/v8config.h: | ||
| 15 | +/Users/fzy/.node-gyp/8.2.1/include/node/node_version.h: | ||
| 16 | +../src/libcoro/coro.h: | ||
| 17 | +../src/v8-version.h: |
| 1 | +cmd_Release/obj.target/fibers/src/libcoro/coro.o := cc '-DNODE_GYP_MODULE_NAME=fibers' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DUSE_CORO' '-DCORO_GUARDPAGES=1' '-DCORO_SJLJ' '-DBUILDING_NODE_EXTENSION' -I/Users/fzy/.node-gyp/8.2.1/include/node -I/Users/fzy/.node-gyp/8.2.1/src -I/Users/fzy/.node-gyp/8.2.1/deps/uv/include -I/Users/fzy/.node-gyp/8.2.1/deps/v8/include -O3 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/fibers/src/libcoro/coro.o.d.raw -c -o Release/obj.target/fibers/src/libcoro/coro.o ../src/libcoro/coro.c | ||
| 2 | +Release/obj.target/fibers/src/libcoro/coro.o: ../src/libcoro/coro.c \ | ||
| 3 | + ../src/libcoro/coro.h | ||
| 4 | +../src/libcoro/coro.c: | ||
| 5 | +../src/libcoro/coro.h: |
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
| 1 | +# Do not edit. File was generated by node-gyp's "configure" step | ||
| 2 | +{ | ||
| 3 | + "target_defaults": { | ||
| 4 | + "cflags": [], | ||
| 5 | + "default_configuration": "Release", | ||
| 6 | + "defines": [], | ||
| 7 | + "include_dirs": [], | ||
| 8 | + "libraries": [] | ||
| 9 | + }, | ||
| 10 | + "variables": { | ||
| 11 | + "asan": 0, | ||
| 12 | + "coverage": "false", | ||
| 13 | + "debug_devtools": "node", | ||
| 14 | + "force_dynamic_crt": 0, | ||
| 15 | + "host_arch": "x64", | ||
| 16 | + "icu_data_file": "icudt59l.dat", | ||
| 17 | + "icu_data_in": "../../deps/icu-small/source/data/in/icudt59l.dat", | ||
| 18 | + "icu_endianness": "l", | ||
| 19 | + "icu_gyp_path": "tools/icu/icu-generic.gyp", | ||
| 20 | + "icu_locales": "en,root", | ||
| 21 | + "icu_path": "deps/icu-small", | ||
| 22 | + "icu_small": "true", | ||
| 23 | + "icu_ver_major": "59", | ||
| 24 | + "llvm_version": 0, | ||
| 25 | + "node_byteorder": "little", | ||
| 26 | + "node_enable_d8": "false", | ||
| 27 | + "node_enable_v8_vtunejit": "false", | ||
| 28 | + "node_install_npm": "true", | ||
| 29 | + "node_module_version": 57, | ||
| 30 | + "node_no_browser_globals": "false", | ||
| 31 | + "node_prefix": "/", | ||
| 32 | + "node_release_urlbase": "https://nodejs.org/download/release/", | ||
| 33 | + "node_shared": "false", | ||
| 34 | + "node_shared_cares": "false", | ||
| 35 | + "node_shared_http_parser": "false", | ||
| 36 | + "node_shared_libuv": "false", | ||
| 37 | + "node_shared_openssl": "false", | ||
| 38 | + "node_shared_zlib": "false", | ||
| 39 | + "node_tag": "", | ||
| 40 | + "node_use_bundled_v8": "true", | ||
| 41 | + "node_use_dtrace": "true", | ||
| 42 | + "node_use_etw": "false", | ||
| 43 | + "node_use_lttng": "false", | ||
| 44 | + "node_use_openssl": "true", | ||
| 45 | + "node_use_perfctr": "false", | ||
| 46 | + "node_use_v8_platform": "true", | ||
| 47 | + "node_without_node_options": "false", | ||
| 48 | + "openssl_fips": "", | ||
| 49 | + "openssl_no_asm": 0, | ||
| 50 | + "shlib_suffix": "57.dylib", | ||
| 51 | + "target_arch": "x64", | ||
| 52 | + "uv_parent_path": "/deps/uv/", | ||
| 53 | + "uv_use_dtrace": "true", | ||
| 54 | + "v8_enable_gdbjit": 0, | ||
| 55 | + "v8_enable_i18n_support": 1, | ||
| 56 | + "v8_enable_inspector": 1, | ||
| 57 | + "v8_no_strict_aliasing": 1, | ||
| 58 | + "v8_optimized_debug": 0, | ||
| 59 | + "v8_promise_internal_field_count": 1, | ||
| 60 | + "v8_random_seed": 0, | ||
| 61 | + "v8_trace_maps": 0, | ||
| 62 | + "v8_use_snapshot": "false", | ||
| 63 | + "want_separate_host_toolset": 0, | ||
| 64 | + "want_separate_host_toolset_mkpeephole": 0, | ||
| 65 | + "xcode_version": "7.0", | ||
| 66 | + "nodedir": "/Users/fzy/.node-gyp/8.2.1", | ||
| 67 | + "copy_dev_lib": "true", | ||
| 68 | + "standalone_static_library": 1, | ||
| 69 | + "registry": "https://registry.npm.taobao.org", | ||
| 70 | + "userconfig": "/Users/fzy/.cnpmrc", | ||
| 71 | + "r": "https://registry.npm.taobao.org", | ||
| 72 | + "disturl": "https://npm.taobao.org/mirrors/node", | ||
| 73 | + "cache": "/Users/fzy/.npminstall_tarball" | ||
| 74 | + } | ||
| 75 | +} |
| 1 | +# This file is generated by gyp; do not edit. | ||
| 2 | + | ||
| 3 | +TOOLSET := target | ||
| 4 | +TARGET := fibers | ||
| 5 | +DEFS_Debug := \ | ||
| 6 | + '-DNODE_GYP_MODULE_NAME=fibers' \ | ||
| 7 | + '-DUSING_UV_SHARED=1' \ | ||
| 8 | + '-DUSING_V8_SHARED=1' \ | ||
| 9 | + '-DV8_DEPRECATION_WARNINGS=1' \ | ||
| 10 | + '-D_DARWIN_USE_64_BIT_INODE=1' \ | ||
| 11 | + '-D_LARGEFILE_SOURCE' \ | ||
| 12 | + '-D_FILE_OFFSET_BITS=64' \ | ||
| 13 | + '-DUSE_CORO' \ | ||
| 14 | + '-DCORO_GUARDPAGES=1' \ | ||
| 15 | + '-DCORO_SJLJ' \ | ||
| 16 | + '-DBUILDING_NODE_EXTENSION' \ | ||
| 17 | + '-DDEBUG' \ | ||
| 18 | + '-D_DEBUG' \ | ||
| 19 | + '-DV8_ENABLE_CHECKS' | ||
| 20 | + | ||
| 21 | +# Flags passed to all source files. | ||
| 22 | +CFLAGS_Debug := \ | ||
| 23 | + -O0 \ | ||
| 24 | + -gdwarf-2 \ | ||
| 25 | + -mmacosx-version-min=10.7 \ | ||
| 26 | + -arch x86_64 \ | ||
| 27 | + -Wall \ | ||
| 28 | + -Wendif-labels \ | ||
| 29 | + -W \ | ||
| 30 | + -Wno-unused-parameter | ||
| 31 | + | ||
| 32 | +# Flags passed to only C files. | ||
| 33 | +CFLAGS_C_Debug := \ | ||
| 34 | + -fno-strict-aliasing | ||
| 35 | + | ||
| 36 | +# Flags passed to only C++ files. | ||
| 37 | +CFLAGS_CC_Debug := \ | ||
| 38 | + -std=gnu++0x \ | ||
| 39 | + -stdlib=libc++ \ | ||
| 40 | + -fno-rtti \ | ||
| 41 | + -fno-exceptions \ | ||
| 42 | + -fno-threadsafe-statics \ | ||
| 43 | + -fno-strict-aliasing | ||
| 44 | + | ||
| 45 | +# Flags passed to only ObjC files. | ||
| 46 | +CFLAGS_OBJC_Debug := | ||
| 47 | + | ||
| 48 | +# Flags passed to only ObjC++ files. | ||
| 49 | +CFLAGS_OBJCC_Debug := | ||
| 50 | + | ||
| 51 | +INCS_Debug := \ | ||
| 52 | + -I/Users/fzy/.node-gyp/8.2.1/include/node \ | ||
| 53 | + -I/Users/fzy/.node-gyp/8.2.1/src \ | ||
| 54 | + -I/Users/fzy/.node-gyp/8.2.1/deps/uv/include \ | ||
| 55 | + -I/Users/fzy/.node-gyp/8.2.1/deps/v8/include | ||
| 56 | + | ||
| 57 | +DEFS_Release := \ | ||
| 58 | + '-DNODE_GYP_MODULE_NAME=fibers' \ | ||
| 59 | + '-DUSING_UV_SHARED=1' \ | ||
| 60 | + '-DUSING_V8_SHARED=1' \ | ||
| 61 | + '-DV8_DEPRECATION_WARNINGS=1' \ | ||
| 62 | + '-D_DARWIN_USE_64_BIT_INODE=1' \ | ||
| 63 | + '-D_LARGEFILE_SOURCE' \ | ||
| 64 | + '-D_FILE_OFFSET_BITS=64' \ | ||
| 65 | + '-DUSE_CORO' \ | ||
| 66 | + '-DCORO_GUARDPAGES=1' \ | ||
| 67 | + '-DCORO_SJLJ' \ | ||
| 68 | + '-DBUILDING_NODE_EXTENSION' | ||
| 69 | + | ||
| 70 | +# Flags passed to all source files. | ||
| 71 | +CFLAGS_Release := \ | ||
| 72 | + -O3 \ | ||
| 73 | + -mmacosx-version-min=10.7 \ | ||
| 74 | + -arch x86_64 \ | ||
| 75 | + -Wall \ | ||
| 76 | + -Wendif-labels \ | ||
| 77 | + -W \ | ||
| 78 | + -Wno-unused-parameter | ||
| 79 | + | ||
| 80 | +# Flags passed to only C files. | ||
| 81 | +CFLAGS_C_Release := \ | ||
| 82 | + -fno-strict-aliasing | ||
| 83 | + | ||
| 84 | +# Flags passed to only C++ files. | ||
| 85 | +CFLAGS_CC_Release := \ | ||
| 86 | + -std=gnu++0x \ | ||
| 87 | + -stdlib=libc++ \ | ||
| 88 | + -fno-rtti \ | ||
| 89 | + -fno-exceptions \ | ||
| 90 | + -fno-threadsafe-statics \ | ||
| 91 | + -fno-strict-aliasing | ||
| 92 | + | ||
| 93 | +# Flags passed to only ObjC files. | ||
| 94 | +CFLAGS_OBJC_Release := | ||
| 95 | + | ||
| 96 | +# Flags passed to only ObjC++ files. | ||
| 97 | +CFLAGS_OBJCC_Release := | ||
| 98 | + | ||
| 99 | +INCS_Release := \ | ||
| 100 | + -I/Users/fzy/.node-gyp/8.2.1/include/node \ | ||
| 101 | + -I/Users/fzy/.node-gyp/8.2.1/src \ | ||
| 102 | + -I/Users/fzy/.node-gyp/8.2.1/deps/uv/include \ | ||
| 103 | + -I/Users/fzy/.node-gyp/8.2.1/deps/v8/include | ||
| 104 | + | ||
| 105 | +OBJS := \ | ||
| 106 | + $(obj).target/$(TARGET)/src/fibers.o \ | ||
| 107 | + $(obj).target/$(TARGET)/src/coroutine.o \ | ||
| 108 | + $(obj).target/$(TARGET)/src/libcoro/coro.o | ||
| 109 | + | ||
| 110 | +# Add to the list of files we specially track dependencies for. | ||
| 111 | +all_deps += $(OBJS) | ||
| 112 | + | ||
| 113 | +# CFLAGS et al overrides must be target-local. | ||
| 114 | +# See "Target-specific Variable Values" in the GNU Make manual. | ||
| 115 | +$(OBJS): TOOLSET := $(TOOLSET) | ||
| 116 | +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) | ||
| 117 | +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) | ||
| 118 | +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) | ||
| 119 | +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) | ||
| 120 | + | ||
| 121 | +# Suffix rules, putting all outputs into $(obj). | ||
| 122 | + | ||
| 123 | +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD | ||
| 124 | + @$(call do_cmd,cxx,1) | ||
| 125 | + | ||
| 126 | +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD | ||
| 127 | + @$(call do_cmd,cc,1) | ||
| 128 | + | ||
| 129 | +# Try building from generated source, too. | ||
| 130 | + | ||
| 131 | +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD | ||
| 132 | + @$(call do_cmd,cxx,1) | ||
| 133 | + | ||
| 134 | +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD | ||
| 135 | + @$(call do_cmd,cc,1) | ||
| 136 | + | ||
| 137 | +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD | ||
| 138 | + @$(call do_cmd,cxx,1) | ||
| 139 | + | ||
| 140 | +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD | ||
| 141 | + @$(call do_cmd,cc,1) | ||
| 142 | + | ||
| 143 | +# End of this set of suffix rules | ||
| 144 | +### Rules for final target. | ||
| 145 | +LDFLAGS_Debug := \ | ||
| 146 | + -undefined dynamic_lookup \ | ||
| 147 | + -Wl,-no_pie \ | ||
| 148 | + -Wl,-search_paths_first \ | ||
| 149 | + -mmacosx-version-min=10.7 \ | ||
| 150 | + -arch x86_64 \ | ||
| 151 | + -L$(builddir) \ | ||
| 152 | + -stdlib=libc++ | ||
| 153 | + | ||
| 154 | +LIBTOOLFLAGS_Debug := \ | ||
| 155 | + -undefined dynamic_lookup \ | ||
| 156 | + -Wl,-no_pie \ | ||
| 157 | + -Wl,-search_paths_first | ||
| 158 | + | ||
| 159 | +LDFLAGS_Release := \ | ||
| 160 | + -undefined dynamic_lookup \ | ||
| 161 | + -Wl,-no_pie \ | ||
| 162 | + -Wl,-search_paths_first \ | ||
| 163 | + -mmacosx-version-min=10.7 \ | ||
| 164 | + -arch x86_64 \ | ||
| 165 | + -L$(builddir) \ | ||
| 166 | + -stdlib=libc++ | ||
| 167 | + | ||
| 168 | +LIBTOOLFLAGS_Release := \ | ||
| 169 | + -undefined dynamic_lookup \ | ||
| 170 | + -Wl,-no_pie \ | ||
| 171 | + -Wl,-search_paths_first | ||
| 172 | + | ||
| 173 | +LIBS := | ||
| 174 | + | ||
| 175 | +$(builddir)/fibers.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) | ||
| 176 | +$(builddir)/fibers.node: LIBS := $(LIBS) | ||
| 177 | +$(builddir)/fibers.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE)) | ||
| 178 | +$(builddir)/fibers.node: TOOLSET := $(TOOLSET) | ||
| 179 | +$(builddir)/fibers.node: $(OBJS) FORCE_DO_CMD | ||
| 180 | + $(call do_cmd,solink_module) | ||
| 181 | + | ||
| 182 | +all_deps += $(builddir)/fibers.node | ||
| 183 | +# Add target alias | ||
| 184 | +.PHONY: fibers | ||
| 185 | +fibers: $(builddir)/fibers.node | ||
| 186 | + | ||
| 187 | +# Short alias for building this executable. | ||
| 188 | +.PHONY: fibers.node | ||
| 189 | +fibers.node: $(builddir)/fibers.node | ||
| 190 | + | ||
| 191 | +# Add executable to "all" target. | ||
| 192 | +.PHONY: all | ||
| 193 | +all: $(builddir)/fibers.node | ||
| 194 | + |
| 1 | +#!/usr/bin/env python | ||
| 2 | +# Generated by gyp. Do not edit. | ||
| 3 | +# Copyright (c) 2012 Google Inc. All rights reserved. | ||
| 4 | +# Use of this source code is governed by a BSD-style license that can be | ||
| 5 | +# found in the LICENSE file. | ||
| 6 | + | ||
| 7 | +"""Utility functions to perform Xcode-style build steps. | ||
| 8 | + | ||
| 9 | +These functions are executed via gyp-mac-tool when using the Makefile generator. | ||
| 10 | +""" | ||
| 11 | + | ||
| 12 | +import fcntl | ||
| 13 | +import fnmatch | ||
| 14 | +import glob | ||
| 15 | +import json | ||
| 16 | +import os | ||
| 17 | +import plistlib | ||
| 18 | +import re | ||
| 19 | +import shutil | ||
| 20 | +import string | ||
| 21 | +import subprocess | ||
| 22 | +import sys | ||
| 23 | +import tempfile | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +def main(args): | ||
| 27 | + executor = MacTool() | ||
| 28 | + exit_code = executor.Dispatch(args) | ||
| 29 | + if exit_code is not None: | ||
| 30 | + sys.exit(exit_code) | ||
| 31 | + | ||
| 32 | + | ||
| 33 | +class MacTool(object): | ||
| 34 | + """This class performs all the Mac tooling steps. The methods can either be | ||
| 35 | + executed directly, or dispatched from an argument list.""" | ||
| 36 | + | ||
| 37 | + def Dispatch(self, args): | ||
| 38 | + """Dispatches a string command to a method.""" | ||
| 39 | + if len(args) < 1: | ||
| 40 | + raise Exception("Not enough arguments") | ||
| 41 | + | ||
| 42 | + method = "Exec%s" % self._CommandifyName(args[0]) | ||
| 43 | + return getattr(self, method)(*args[1:]) | ||
| 44 | + | ||
| 45 | + def _CommandifyName(self, name_string): | ||
| 46 | + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" | ||
| 47 | + return name_string.title().replace('-', '') | ||
| 48 | + | ||
| 49 | + def ExecCopyBundleResource(self, source, dest, convert_to_binary): | ||
| 50 | + """Copies a resource file to the bundle/Resources directory, performing any | ||
| 51 | + necessary compilation on each resource.""" | ||
| 52 | + extension = os.path.splitext(source)[1].lower() | ||
| 53 | + if os.path.isdir(source): | ||
| 54 | + # Copy tree. | ||
| 55 | + # TODO(thakis): This copies file attributes like mtime, while the | ||
| 56 | + # single-file branch below doesn't. This should probably be changed to | ||
| 57 | + # be consistent with the single-file branch. | ||
| 58 | + if os.path.exists(dest): | ||
| 59 | + shutil.rmtree(dest) | ||
| 60 | + shutil.copytree(source, dest) | ||
| 61 | + elif extension == '.xib': | ||
| 62 | + return self._CopyXIBFile(source, dest) | ||
| 63 | + elif extension == '.storyboard': | ||
| 64 | + return self._CopyXIBFile(source, dest) | ||
| 65 | + elif extension == '.strings': | ||
| 66 | + self._CopyStringsFile(source, dest, convert_to_binary) | ||
| 67 | + else: | ||
| 68 | + shutil.copy(source, dest) | ||
| 69 | + | ||
| 70 | + def _CopyXIBFile(self, source, dest): | ||
| 71 | + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" | ||
| 72 | + | ||
| 73 | + # ibtool sometimes crashes with relative paths. See crbug.com/314728. | ||
| 74 | + base = os.path.dirname(os.path.realpath(__file__)) | ||
| 75 | + if os.path.relpath(source): | ||
| 76 | + source = os.path.join(base, source) | ||
| 77 | + if os.path.relpath(dest): | ||
| 78 | + dest = os.path.join(base, dest) | ||
| 79 | + | ||
| 80 | + args = ['xcrun', 'ibtool', '--errors', '--warnings', '--notices', | ||
| 81 | + '--output-format', 'human-readable-text', '--compile', dest, source] | ||
| 82 | + ibtool_section_re = re.compile(r'/\*.*\*/') | ||
| 83 | + ibtool_re = re.compile(r'.*note:.*is clipping its content') | ||
| 84 | + ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) | ||
| 85 | + current_section_header = None | ||
| 86 | + for line in ibtoolout.stdout: | ||
| 87 | + if ibtool_section_re.match(line): | ||
| 88 | + current_section_header = line | ||
| 89 | + elif not ibtool_re.match(line): | ||
| 90 | + if current_section_header: | ||
| 91 | + sys.stdout.write(current_section_header) | ||
| 92 | + current_section_header = None | ||
| 93 | + sys.stdout.write(line) | ||
| 94 | + return ibtoolout.returncode | ||
| 95 | + | ||
| 96 | + def _ConvertToBinary(self, dest): | ||
| 97 | + subprocess.check_call([ | ||
| 98 | + 'xcrun', 'plutil', '-convert', 'binary1', '-o', dest, dest]) | ||
| 99 | + | ||
| 100 | + def _CopyStringsFile(self, source, dest, convert_to_binary): | ||
| 101 | + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" | ||
| 102 | + input_code = self._DetectInputEncoding(source) or "UTF-8" | ||
| 103 | + | ||
| 104 | + # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call | ||
| 105 | + # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints | ||
| 106 | + # CFPropertyListCreateFromXMLData(): Old-style plist parser: missing | ||
| 107 | + # semicolon in dictionary. | ||
| 108 | + # on invalid files. Do the same kind of validation. | ||
| 109 | + import CoreFoundation | ||
| 110 | + s = open(source, 'rb').read() | ||
| 111 | + d = CoreFoundation.CFDataCreate(None, s, len(s)) | ||
| 112 | + _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None) | ||
| 113 | + if error: | ||
| 114 | + return | ||
| 115 | + | ||
| 116 | + fp = open(dest, 'wb') | ||
| 117 | + fp.write(s.decode(input_code).encode('UTF-16')) | ||
| 118 | + fp.close() | ||
| 119 | + | ||
| 120 | + if convert_to_binary == 'True': | ||
| 121 | + self._ConvertToBinary(dest) | ||
| 122 | + | ||
| 123 | + def _DetectInputEncoding(self, file_name): | ||
| 124 | + """Reads the first few bytes from file_name and tries to guess the text | ||
| 125 | + encoding. Returns None as a guess if it can't detect it.""" | ||
| 126 | + fp = open(file_name, 'rb') | ||
| 127 | + try: | ||
| 128 | + header = fp.read(3) | ||
| 129 | + except e: | ||
| 130 | + fp.close() | ||
| 131 | + return None | ||
| 132 | + fp.close() | ||
| 133 | + if header.startswith("\xFE\xFF"): | ||
| 134 | + return "UTF-16" | ||
| 135 | + elif header.startswith("\xFF\xFE"): | ||
| 136 | + return "UTF-16" | ||
| 137 | + elif header.startswith("\xEF\xBB\xBF"): | ||
| 138 | + return "UTF-8" | ||
| 139 | + else: | ||
| 140 | + return None | ||
| 141 | + | ||
| 142 | + def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys): | ||
| 143 | + """Copies the |source| Info.plist to the destination directory |dest|.""" | ||
| 144 | + # Read the source Info.plist into memory. | ||
| 145 | + fd = open(source, 'r') | ||
| 146 | + lines = fd.read() | ||
| 147 | + fd.close() | ||
| 148 | + | ||
| 149 | + # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild). | ||
| 150 | + plist = plistlib.readPlistFromString(lines) | ||
| 151 | + if keys: | ||
| 152 | + plist = dict(plist.items() + json.loads(keys[0]).items()) | ||
| 153 | + lines = plistlib.writePlistToString(plist) | ||
| 154 | + | ||
| 155 | + # Go through all the environment variables and replace them as variables in | ||
| 156 | + # the file. | ||
| 157 | + IDENT_RE = re.compile(r'[/\s]') | ||
| 158 | + for key in os.environ: | ||
| 159 | + if key.startswith('_'): | ||
| 160 | + continue | ||
| 161 | + evar = '${%s}' % key | ||
| 162 | + evalue = os.environ[key] | ||
| 163 | + lines = string.replace(lines, evar, evalue) | ||
| 164 | + | ||
| 165 | + # Xcode supports various suffices on environment variables, which are | ||
| 166 | + # all undocumented. :rfc1034identifier is used in the standard project | ||
| 167 | + # template these days, and :identifier was used earlier. They are used to | ||
| 168 | + # convert non-url characters into things that look like valid urls -- | ||
| 169 | + # except that the replacement character for :identifier, '_' isn't valid | ||
| 170 | + # in a URL either -- oops, hence :rfc1034identifier was born. | ||
| 171 | + evar = '${%s:identifier}' % key | ||
| 172 | + evalue = IDENT_RE.sub('_', os.environ[key]) | ||
| 173 | + lines = string.replace(lines, evar, evalue) | ||
| 174 | + | ||
| 175 | + evar = '${%s:rfc1034identifier}' % key | ||
| 176 | + evalue = IDENT_RE.sub('-', os.environ[key]) | ||
| 177 | + lines = string.replace(lines, evar, evalue) | ||
| 178 | + | ||
| 179 | + # Remove any keys with values that haven't been replaced. | ||
| 180 | + lines = lines.split('\n') | ||
| 181 | + for i in range(len(lines)): | ||
| 182 | + if lines[i].strip().startswith("<string>${"): | ||
| 183 | + lines[i] = None | ||
| 184 | + lines[i - 1] = None | ||
| 185 | + lines = '\n'.join(filter(lambda x: x is not None, lines)) | ||
| 186 | + | ||
| 187 | + # Write out the file with variables replaced. | ||
| 188 | + fd = open(dest, 'w') | ||
| 189 | + fd.write(lines) | ||
| 190 | + fd.close() | ||
| 191 | + | ||
| 192 | + # Now write out PkgInfo file now that the Info.plist file has been | ||
| 193 | + # "compiled". | ||
| 194 | + self._WritePkgInfo(dest) | ||
| 195 | + | ||
| 196 | + if convert_to_binary == 'True': | ||
| 197 | + self._ConvertToBinary(dest) | ||
| 198 | + | ||
| 199 | + def _WritePkgInfo(self, info_plist): | ||
| 200 | + """This writes the PkgInfo file from the data stored in Info.plist.""" | ||
| 201 | + plist = plistlib.readPlist(info_plist) | ||
| 202 | + if not plist: | ||
| 203 | + return | ||
| 204 | + | ||
| 205 | + # Only create PkgInfo for executable types. | ||
| 206 | + package_type = plist['CFBundlePackageType'] | ||
| 207 | + if package_type != 'APPL': | ||
| 208 | + return | ||
| 209 | + | ||
| 210 | + # The format of PkgInfo is eight characters, representing the bundle type | ||
| 211 | + # and bundle signature, each four characters. If that is missing, four | ||
| 212 | + # '?' characters are used instead. | ||
| 213 | + signature_code = plist.get('CFBundleSignature', '????') | ||
| 214 | + if len(signature_code) != 4: # Wrong length resets everything, too. | ||
| 215 | + signature_code = '?' * 4 | ||
| 216 | + | ||
| 217 | + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') | ||
| 218 | + fp = open(dest, 'w') | ||
| 219 | + fp.write('%s%s' % (package_type, signature_code)) | ||
| 220 | + fp.close() | ||
| 221 | + | ||
| 222 | + def ExecFlock(self, lockfile, *cmd_list): | ||
| 223 | + """Emulates the most basic behavior of Linux's flock(1).""" | ||
| 224 | + # Rely on exception handling to report errors. | ||
| 225 | + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) | ||
| 226 | + fcntl.flock(fd, fcntl.LOCK_EX) | ||
| 227 | + return subprocess.call(cmd_list) | ||
| 228 | + | ||
| 229 | + def ExecFilterLibtool(self, *cmd_list): | ||
| 230 | + """Calls libtool and filters out '/path/to/libtool: file: foo.o has no | ||
| 231 | + symbols'.""" | ||
| 232 | + libtool_re = re.compile(r'^.*libtool: file: .* has no symbols$') | ||
| 233 | + libtool_re5 = re.compile( | ||
| 234 | + r'^.*libtool: warning for library: ' + | ||
| 235 | + r'.* the table of contents is empty ' + | ||
| 236 | + r'\(no object file members in the library define global symbols\)$') | ||
| 237 | + env = os.environ.copy() | ||
| 238 | + # Ref: | ||
| 239 | + # http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c | ||
| 240 | + # The problem with this flag is that it resets the file mtime on the file to | ||
| 241 | + # epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone. | ||
| 242 | + env['ZERO_AR_DATE'] = '1' | ||
| 243 | + libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) | ||
| 244 | + _, err = libtoolout.communicate() | ||
| 245 | + for line in err.splitlines(): | ||
| 246 | + if not libtool_re.match(line) and not libtool_re5.match(line): | ||
| 247 | + print >>sys.stderr, line | ||
| 248 | + # Unconditionally touch the output .a file on the command line if present | ||
| 249 | + # and the command succeeded. A bit hacky. | ||
| 250 | + if not libtoolout.returncode: | ||
| 251 | + for i in range(len(cmd_list) - 1): | ||
| 252 | + if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'): | ||
| 253 | + os.utime(cmd_list[i+1], None) | ||
| 254 | + break | ||
| 255 | + return libtoolout.returncode | ||
| 256 | + | ||
| 257 | + def ExecPackageFramework(self, framework, version): | ||
| 258 | + """Takes a path to Something.framework and the Current version of that and | ||
| 259 | + sets up all the symlinks.""" | ||
| 260 | + # Find the name of the binary based on the part before the ".framework". | ||
| 261 | + binary = os.path.basename(framework).split('.')[0] | ||
| 262 | + | ||
| 263 | + CURRENT = 'Current' | ||
| 264 | + RESOURCES = 'Resources' | ||
| 265 | + VERSIONS = 'Versions' | ||
| 266 | + | ||
| 267 | + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): | ||
| 268 | + # Binary-less frameworks don't seem to contain symlinks (see e.g. | ||
| 269 | + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). | ||
| 270 | + return | ||
| 271 | + | ||
| 272 | + # Move into the framework directory to set the symlinks correctly. | ||
| 273 | + pwd = os.getcwd() | ||
| 274 | + os.chdir(framework) | ||
| 275 | + | ||
| 276 | + # Set up the Current version. | ||
| 277 | + self._Relink(version, os.path.join(VERSIONS, CURRENT)) | ||
| 278 | + | ||
| 279 | + # Set up the root symlinks. | ||
| 280 | + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) | ||
| 281 | + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) | ||
| 282 | + | ||
| 283 | + # Back to where we were before! | ||
| 284 | + os.chdir(pwd) | ||
| 285 | + | ||
| 286 | + def _Relink(self, dest, link): | ||
| 287 | + """Creates a symlink to |dest| named |link|. If |link| already exists, | ||
| 288 | + it is overwritten.""" | ||
| 289 | + if os.path.lexists(link): | ||
| 290 | + os.remove(link) | ||
| 291 | + os.symlink(dest, link) | ||
| 292 | + | ||
| 293 | + def ExecCompileXcassets(self, keys, *inputs): | ||
| 294 | + """Compiles multiple .xcassets files into a single .car file. | ||
| 295 | + | ||
| 296 | + This invokes 'actool' to compile all the inputs .xcassets files. The | ||
| 297 | + |keys| arguments is a json-encoded dictionary of extra arguments to | ||
| 298 | + pass to 'actool' when the asset catalogs contains an application icon | ||
| 299 | + or a launch image. | ||
| 300 | + | ||
| 301 | + Note that 'actool' does not create the Assets.car file if the asset | ||
| 302 | + catalogs does not contains imageset. | ||
| 303 | + """ | ||
| 304 | + command_line = [ | ||
| 305 | + 'xcrun', 'actool', '--output-format', 'human-readable-text', | ||
| 306 | + '--compress-pngs', '--notices', '--warnings', '--errors', | ||
| 307 | + ] | ||
| 308 | + is_iphone_target = 'IPHONEOS_DEPLOYMENT_TARGET' in os.environ | ||
| 309 | + if is_iphone_target: | ||
| 310 | + platform = os.environ['CONFIGURATION'].split('-')[-1] | ||
| 311 | + if platform not in ('iphoneos', 'iphonesimulator'): | ||
| 312 | + platform = 'iphonesimulator' | ||
| 313 | + command_line.extend([ | ||
| 314 | + '--platform', platform, '--target-device', 'iphone', | ||
| 315 | + '--target-device', 'ipad', '--minimum-deployment-target', | ||
| 316 | + os.environ['IPHONEOS_DEPLOYMENT_TARGET'], '--compile', | ||
| 317 | + os.path.abspath(os.environ['CONTENTS_FOLDER_PATH']), | ||
| 318 | + ]) | ||
| 319 | + else: | ||
| 320 | + command_line.extend([ | ||
| 321 | + '--platform', 'macosx', '--target-device', 'mac', | ||
| 322 | + '--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'], | ||
| 323 | + '--compile', | ||
| 324 | + os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']), | ||
| 325 | + ]) | ||
| 326 | + if keys: | ||
| 327 | + keys = json.loads(keys) | ||
| 328 | + for key, value in keys.iteritems(): | ||
| 329 | + arg_name = '--' + key | ||
| 330 | + if isinstance(value, bool): | ||
| 331 | + if value: | ||
| 332 | + command_line.append(arg_name) | ||
| 333 | + elif isinstance(value, list): | ||
| 334 | + for v in value: | ||
| 335 | + command_line.append(arg_name) | ||
| 336 | + command_line.append(str(v)) | ||
| 337 | + else: | ||
| 338 | + command_line.append(arg_name) | ||
| 339 | + command_line.append(str(value)) | ||
| 340 | + # Note: actool crashes if inputs path are relative, so use os.path.abspath | ||
| 341 | + # to get absolute path name for inputs. | ||
| 342 | + command_line.extend(map(os.path.abspath, inputs)) | ||
| 343 | + subprocess.check_call(command_line) | ||
| 344 | + | ||
| 345 | + def ExecMergeInfoPlist(self, output, *inputs): | ||
| 346 | + """Merge multiple .plist files into a single .plist file.""" | ||
| 347 | + merged_plist = {} | ||
| 348 | + for path in inputs: | ||
| 349 | + plist = self._LoadPlistMaybeBinary(path) | ||
| 350 | + self._MergePlist(merged_plist, plist) | ||
| 351 | + plistlib.writePlist(merged_plist, output) | ||
| 352 | + | ||
| 353 | + def ExecCodeSignBundle(self, key, resource_rules, entitlements, provisioning): | ||
| 354 | + """Code sign a bundle. | ||
| 355 | + | ||
| 356 | + This function tries to code sign an iOS bundle, following the same | ||
| 357 | + algorithm as Xcode: | ||
| 358 | + 1. copy ResourceRules.plist from the user or the SDK into the bundle, | ||
| 359 | + 2. pick the provisioning profile that best match the bundle identifier, | ||
| 360 | + and copy it into the bundle as embedded.mobileprovision, | ||
| 361 | + 3. copy Entitlements.plist from user or SDK next to the bundle, | ||
| 362 | + 4. code sign the bundle. | ||
| 363 | + """ | ||
| 364 | + resource_rules_path = self._InstallResourceRules(resource_rules) | ||
| 365 | + substitutions, overrides = self._InstallProvisioningProfile( | ||
| 366 | + provisioning, self._GetCFBundleIdentifier()) | ||
| 367 | + entitlements_path = self._InstallEntitlements( | ||
| 368 | + entitlements, substitutions, overrides) | ||
| 369 | + subprocess.check_call([ | ||
| 370 | + 'codesign', '--force', '--sign', key, '--resource-rules', | ||
| 371 | + resource_rules_path, '--entitlements', entitlements_path, | ||
| 372 | + os.path.join( | ||
| 373 | + os.environ['TARGET_BUILD_DIR'], | ||
| 374 | + os.environ['FULL_PRODUCT_NAME'])]) | ||
| 375 | + | ||
| 376 | + def _InstallResourceRules(self, resource_rules): | ||
| 377 | + """Installs ResourceRules.plist from user or SDK into the bundle. | ||
| 378 | + | ||
| 379 | + Args: | ||
| 380 | + resource_rules: string, optional, path to the ResourceRules.plist file | ||
| 381 | + to use, default to "${SDKROOT}/ResourceRules.plist" | ||
| 382 | + | ||
| 383 | + Returns: | ||
| 384 | + Path to the copy of ResourceRules.plist into the bundle. | ||
| 385 | + """ | ||
| 386 | + source_path = resource_rules | ||
| 387 | + target_path = os.path.join( | ||
| 388 | + os.environ['BUILT_PRODUCTS_DIR'], | ||
| 389 | + os.environ['CONTENTS_FOLDER_PATH'], | ||
| 390 | + 'ResourceRules.plist') | ||
| 391 | + if not source_path: | ||
| 392 | + source_path = os.path.join( | ||
| 393 | + os.environ['SDKROOT'], 'ResourceRules.plist') | ||
| 394 | + shutil.copy2(source_path, target_path) | ||
| 395 | + return target_path | ||
| 396 | + | ||
| 397 | + def _InstallProvisioningProfile(self, profile, bundle_identifier): | ||
| 398 | + """Installs embedded.mobileprovision into the bundle. | ||
| 399 | + | ||
| 400 | + Args: | ||
| 401 | + profile: string, optional, short name of the .mobileprovision file | ||
| 402 | + to use, if empty or the file is missing, the best file installed | ||
| 403 | + will be used | ||
| 404 | + bundle_identifier: string, value of CFBundleIdentifier from Info.plist | ||
| 405 | + | ||
| 406 | + Returns: | ||
| 407 | + A tuple containing two dictionary: variables substitutions and values | ||
| 408 | + to overrides when generating the entitlements file. | ||
| 409 | + """ | ||
| 410 | + source_path, provisioning_data, team_id = self._FindProvisioningProfile( | ||
| 411 | + profile, bundle_identifier) | ||
| 412 | + target_path = os.path.join( | ||
| 413 | + os.environ['BUILT_PRODUCTS_DIR'], | ||
| 414 | + os.environ['CONTENTS_FOLDER_PATH'], | ||
| 415 | + 'embedded.mobileprovision') | ||
| 416 | + shutil.copy2(source_path, target_path) | ||
| 417 | + substitutions = self._GetSubstitutions(bundle_identifier, team_id + '.') | ||
| 418 | + return substitutions, provisioning_data['Entitlements'] | ||
| 419 | + | ||
| 420 | + def _FindProvisioningProfile(self, profile, bundle_identifier): | ||
| 421 | + """Finds the .mobileprovision file to use for signing the bundle. | ||
| 422 | + | ||
| 423 | + Checks all the installed provisioning profiles (or if the user specified | ||
| 424 | + the PROVISIONING_PROFILE variable, only consult it) and select the most | ||
| 425 | + specific that correspond to the bundle identifier. | ||
| 426 | + | ||
| 427 | + Args: | ||
| 428 | + profile: string, optional, short name of the .mobileprovision file | ||
| 429 | + to use, if empty or the file is missing, the best file installed | ||
| 430 | + will be used | ||
| 431 | + bundle_identifier: string, value of CFBundleIdentifier from Info.plist | ||
| 432 | + | ||
| 433 | + Returns: | ||
| 434 | + A tuple of the path to the selected provisioning profile, the data of | ||
| 435 | + the embedded plist in the provisioning profile and the team identifier | ||
| 436 | + to use for code signing. | ||
| 437 | + | ||
| 438 | + Raises: | ||
| 439 | + SystemExit: if no .mobileprovision can be used to sign the bundle. | ||
| 440 | + """ | ||
| 441 | + profiles_dir = os.path.join( | ||
| 442 | + os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') | ||
| 443 | + if not os.path.isdir(profiles_dir): | ||
| 444 | + print >>sys.stderr, ( | ||
| 445 | + 'cannot find mobile provisioning for %s' % bundle_identifier) | ||
| 446 | + sys.exit(1) | ||
| 447 | + provisioning_profiles = None | ||
| 448 | + if profile: | ||
| 449 | + profile_path = os.path.join(profiles_dir, profile + '.mobileprovision') | ||
| 450 | + if os.path.exists(profile_path): | ||
| 451 | + provisioning_profiles = [profile_path] | ||
| 452 | + if not provisioning_profiles: | ||
| 453 | + provisioning_profiles = glob.glob( | ||
| 454 | + os.path.join(profiles_dir, '*.mobileprovision')) | ||
| 455 | + valid_provisioning_profiles = {} | ||
| 456 | + for profile_path in provisioning_profiles: | ||
| 457 | + profile_data = self._LoadProvisioningProfile(profile_path) | ||
| 458 | + app_id_pattern = profile_data.get( | ||
| 459 | + 'Entitlements', {}).get('application-identifier', '') | ||
| 460 | + for team_identifier in profile_data.get('TeamIdentifier', []): | ||
| 461 | + app_id = '%s.%s' % (team_identifier, bundle_identifier) | ||
| 462 | + if fnmatch.fnmatch(app_id, app_id_pattern): | ||
| 463 | + valid_provisioning_profiles[app_id_pattern] = ( | ||
| 464 | + profile_path, profile_data, team_identifier) | ||
| 465 | + if not valid_provisioning_profiles: | ||
| 466 | + print >>sys.stderr, ( | ||
| 467 | + 'cannot find mobile provisioning for %s' % bundle_identifier) | ||
| 468 | + sys.exit(1) | ||
| 469 | + # If the user has multiple provisioning profiles installed that can be | ||
| 470 | + # used for ${bundle_identifier}, pick the most specific one (ie. the | ||
| 471 | + # provisioning profile whose pattern is the longest). | ||
| 472 | + selected_key = max(valid_provisioning_profiles, key=lambda v: len(v)) | ||
| 473 | + return valid_provisioning_profiles[selected_key] | ||
| 474 | + | ||
| 475 | + def _LoadProvisioningProfile(self, profile_path): | ||
| 476 | + """Extracts the plist embedded in a provisioning profile. | ||
| 477 | + | ||
| 478 | + Args: | ||
| 479 | + profile_path: string, path to the .mobileprovision file | ||
| 480 | + | ||
| 481 | + Returns: | ||
| 482 | + Content of the plist embedded in the provisioning profile as a dictionary. | ||
| 483 | + """ | ||
| 484 | + with tempfile.NamedTemporaryFile() as temp: | ||
| 485 | + subprocess.check_call([ | ||
| 486 | + 'security', 'cms', '-D', '-i', profile_path, '-o', temp.name]) | ||
| 487 | + return self._LoadPlistMaybeBinary(temp.name) | ||
| 488 | + | ||
| 489 | + def _MergePlist(self, merged_plist, plist): | ||
| 490 | + """Merge |plist| into |merged_plist|.""" | ||
| 491 | + for key, value in plist.iteritems(): | ||
| 492 | + if isinstance(value, dict): | ||
| 493 | + merged_value = merged_plist.get(key, {}) | ||
| 494 | + if isinstance(merged_value, dict): | ||
| 495 | + self._MergePlist(merged_value, value) | ||
| 496 | + merged_plist[key] = merged_value | ||
| 497 | + else: | ||
| 498 | + merged_plist[key] = value | ||
| 499 | + else: | ||
| 500 | + merged_plist[key] = value | ||
| 501 | + | ||
| 502 | + def _LoadPlistMaybeBinary(self, plist_path): | ||
| 503 | + """Loads into a memory a plist possibly encoded in binary format. | ||
| 504 | + | ||
| 505 | + This is a wrapper around plistlib.readPlist that tries to convert the | ||
| 506 | + plist to the XML format if it can't be parsed (assuming that it is in | ||
| 507 | + the binary format). | ||
| 508 | + | ||
| 509 | + Args: | ||
| 510 | + plist_path: string, path to a plist file, in XML or binary format | ||
| 511 | + | ||
| 512 | + Returns: | ||
| 513 | + Content of the plist as a dictionary. | ||
| 514 | + """ | ||
| 515 | + try: | ||
| 516 | + # First, try to read the file using plistlib that only supports XML, | ||
| 517 | + # and if an exception is raised, convert a temporary copy to XML and | ||
| 518 | + # load that copy. | ||
| 519 | + return plistlib.readPlist(plist_path) | ||
| 520 | + except: | ||
| 521 | + pass | ||
| 522 | + with tempfile.NamedTemporaryFile() as temp: | ||
| 523 | + shutil.copy2(plist_path, temp.name) | ||
| 524 | + subprocess.check_call(['plutil', '-convert', 'xml1', temp.name]) | ||
| 525 | + return plistlib.readPlist(temp.name) | ||
| 526 | + | ||
| 527 | + def _GetSubstitutions(self, bundle_identifier, app_identifier_prefix): | ||
| 528 | + """Constructs a dictionary of variable substitutions for Entitlements.plist. | ||
| 529 | + | ||
| 530 | + Args: | ||
| 531 | + bundle_identifier: string, value of CFBundleIdentifier from Info.plist | ||
| 532 | + app_identifier_prefix: string, value for AppIdentifierPrefix | ||
| 533 | + | ||
| 534 | + Returns: | ||
| 535 | + Dictionary of substitutions to apply when generating Entitlements.plist. | ||
| 536 | + """ | ||
| 537 | + return { | ||
| 538 | + 'CFBundleIdentifier': bundle_identifier, | ||
| 539 | + 'AppIdentifierPrefix': app_identifier_prefix, | ||
| 540 | + } | ||
| 541 | + | ||
| 542 | + def _GetCFBundleIdentifier(self): | ||
| 543 | + """Extracts CFBundleIdentifier value from Info.plist in the bundle. | ||
| 544 | + | ||
| 545 | + Returns: | ||
| 546 | + Value of CFBundleIdentifier in the Info.plist located in the bundle. | ||
| 547 | + """ | ||
| 548 | + info_plist_path = os.path.join( | ||
| 549 | + os.environ['TARGET_BUILD_DIR'], | ||
| 550 | + os.environ['INFOPLIST_PATH']) | ||
| 551 | + info_plist_data = self._LoadPlistMaybeBinary(info_plist_path) | ||
| 552 | + return info_plist_data['CFBundleIdentifier'] | ||
| 553 | + | ||
| 554 | + def _InstallEntitlements(self, entitlements, substitutions, overrides): | ||
| 555 | + """Generates and install the ${BundleName}.xcent entitlements file. | ||
| 556 | + | ||
| 557 | + Expands variables "$(variable)" pattern in the source entitlements file, | ||
| 558 | + add extra entitlements defined in the .mobileprovision file and the copy | ||
| 559 | + the generated plist to "${BundlePath}.xcent". | ||
| 560 | + | ||
| 561 | + Args: | ||
| 562 | + entitlements: string, optional, path to the Entitlements.plist template | ||
| 563 | + to use, defaults to "${SDKROOT}/Entitlements.plist" | ||
| 564 | + substitutions: dictionary, variable substitutions | ||
| 565 | + overrides: dictionary, values to add to the entitlements | ||
| 566 | + | ||
| 567 | + Returns: | ||
| 568 | + Path to the generated entitlements file. | ||
| 569 | + """ | ||
| 570 | + source_path = entitlements | ||
| 571 | + target_path = os.path.join( | ||
| 572 | + os.environ['BUILT_PRODUCTS_DIR'], | ||
| 573 | + os.environ['PRODUCT_NAME'] + '.xcent') | ||
| 574 | + if not source_path: | ||
| 575 | + source_path = os.path.join( | ||
| 576 | + os.environ['SDKROOT'], | ||
| 577 | + 'Entitlements.plist') | ||
| 578 | + shutil.copy2(source_path, target_path) | ||
| 579 | + data = self._LoadPlistMaybeBinary(target_path) | ||
| 580 | + data = self._ExpandVariables(data, substitutions) | ||
| 581 | + if overrides: | ||
| 582 | + for key in overrides: | ||
| 583 | + if key not in data: | ||
| 584 | + data[key] = overrides[key] | ||
| 585 | + plistlib.writePlist(data, target_path) | ||
| 586 | + return target_path | ||
| 587 | + | ||
| 588 | + def _ExpandVariables(self, data, substitutions): | ||
| 589 | + """Expands variables "$(variable)" in data. | ||
| 590 | + | ||
| 591 | + Args: | ||
| 592 | + data: object, can be either string, list or dictionary | ||
| 593 | + substitutions: dictionary, variable substitutions to perform | ||
| 594 | + | ||
| 595 | + Returns: | ||
| 596 | + Copy of data where each references to "$(variable)" has been replaced | ||
| 597 | + by the corresponding value found in substitutions, or left intact if | ||
| 598 | + the key was not found. | ||
| 599 | + """ | ||
| 600 | + if isinstance(data, str): | ||
| 601 | + for key, value in substitutions.iteritems(): | ||
| 602 | + data = data.replace('$(%s)' % key, value) | ||
| 603 | + return data | ||
| 604 | + if isinstance(data, list): | ||
| 605 | + return [self._ExpandVariables(v, substitutions) for v in data] | ||
| 606 | + if isinstance(data, dict): | ||
| 607 | + return {k: self._ExpandVariables(data[k], substitutions) for k in data} | ||
| 608 | + return data | ||
| 609 | + | ||
| 610 | +if __name__ == '__main__': | ||
| 611 | + sys.exit(main(sys.argv[1:])) |
node_modules/_fibers@1.0.15@fibers/fibers.js
0 → 100644
| 1 | +if (process.fiberLib) { | ||
| 2 | + return module.exports = process.fiberLib; | ||
| 3 | +} | ||
| 4 | +var fs = require('fs'), path = require('path'); | ||
| 5 | + | ||
| 6 | +// Seed random numbers [gh-82] | ||
| 7 | +Math.random(); | ||
| 8 | + | ||
| 9 | +// Look for binary for this platform | ||
| 10 | +var modPath = path.join(__dirname, 'bin', process.platform+ '-'+ process.arch+ '-'+ process.versions.modules, 'fibers'); | ||
| 11 | +try { | ||
| 12 | + fs.statSync(modPath+ '.node'); | ||
| 13 | +} catch (ex) { | ||
| 14 | + // No binary! | ||
| 15 | + console.error( | ||
| 16 | + '## There is an issue with `node-fibers` ##\n'+ | ||
| 17 | + '`'+ modPath+ '.node` is missing.\n\n'+ | ||
| 18 | + 'Try running this to fix the issue: '+ process.execPath+ ' '+ __dirname.replace(' ', '\\ ')+ '/build' | ||
| 19 | + ); | ||
| 20 | + throw new Error('Missing binary. See message above.'); | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +// Pull in fibers implementation | ||
| 24 | +process.fiberLib = module.exports = require(modPath).Fiber; |
node_modules/_fibers@1.0.15@fibers/future.js
0 → 100644
| 1 | +"use strict"; | ||
| 2 | +var Fiber = require('./fibers'); | ||
| 3 | +var util = require('util'); | ||
| 4 | +module.exports = Future; | ||
| 5 | +Function.prototype.future = function(detach) { | ||
| 6 | + var fn = this; | ||
| 7 | + var ret = function() { | ||
| 8 | + var future = new FiberFuture(fn, this, arguments); | ||
| 9 | + if (detach) { | ||
| 10 | + future.detach(); | ||
| 11 | + } | ||
| 12 | + return future; | ||
| 13 | + }; | ||
| 14 | + ret.toString = function() { | ||
| 15 | + return '<<Future '+ fn+ '.future()>>'; | ||
| 16 | + }; | ||
| 17 | + return ret; | ||
| 18 | +}; | ||
| 19 | + | ||
| 20 | +function Future() {} | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * Run a function(s) in a future context, and return a future to their return value. This is useful | ||
| 24 | + * for instances where you want a closure to be able to `.wait()`. This also lets you wait for | ||
| 25 | + * mulitple parallel opertions to run. | ||
| 26 | + */ | ||
| 27 | +Future.task = function(fn) { | ||
| 28 | + if (arguments.length === 1) { | ||
| 29 | + return fn.future()(); | ||
| 30 | + } else { | ||
| 31 | + var future = new Future, pending = arguments.length, error, values = new Array(arguments.length); | ||
| 32 | + for (var ii = 0; ii < arguments.length; ++ii) { | ||
| 33 | + arguments[ii].future()().resolve(function(ii, err, val) { | ||
| 34 | + if (err) { | ||
| 35 | + error = err; | ||
| 36 | + } | ||
| 37 | + values[ii] = val; | ||
| 38 | + if (--pending === 0) { | ||
| 39 | + if (error) { | ||
| 40 | + future.throw(error); | ||
| 41 | + } else { | ||
| 42 | + future.return(values); | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + }.bind(null, ii)); | ||
| 46 | + } | ||
| 47 | + return future; | ||
| 48 | + } | ||
| 49 | +}; | ||
| 50 | + | ||
| 51 | +/** | ||
| 52 | + * Wrap node-style async functions to instead return futures. This assumes that the last parameter | ||
| 53 | + * of the function is a callback. | ||
| 54 | + * | ||
| 55 | + * If a single function is passed a future-returning function is created. If an object is passed a | ||
| 56 | + * new object is returned with all functions wrapped. | ||
| 57 | + * | ||
| 58 | + * The value that is returned from the invocation of the underlying function is assigned to the | ||
| 59 | + * property `_` on the future. This is useful for functions like `execFile` which take a callback, | ||
| 60 | + * but also return meaningful information. | ||
| 61 | + * | ||
| 62 | + * `multi` indicates that this callback will return more than 1 argument after `err`. For example, | ||
| 63 | + * `child_process.exec()` | ||
| 64 | + * | ||
| 65 | + * `suffix` will append a string to every method that was overridden, if you pass an object to | ||
| 66 | + * `Future.wrap()`. Default is 'Future'. | ||
| 67 | + * | ||
| 68 | + * var readFileFuture = Future.wrap(require('fs').readFile); | ||
| 69 | + * var fs = Future.wrap(require('fs')); | ||
| 70 | + * fs.readFileFuture('example.txt').wait(); | ||
| 71 | + */ | ||
| 72 | +Future.wrap = function(fnOrObject, multi, suffix, stop) { | ||
| 73 | + if (typeof fnOrObject === 'object') { | ||
| 74 | + var wrapped = Object.create(fnOrObject); | ||
| 75 | + for (var ii in fnOrObject) { | ||
| 76 | + if (wrapped[ii] instanceof Function) { | ||
| 77 | + wrapped[suffix === undefined ? ii+ 'Future' : ii+ suffix] = Future.wrap(wrapped[ii], multi, suffix, stop); | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + return wrapped; | ||
| 81 | + } else if (typeof fnOrObject === 'function') { | ||
| 82 | + var fn = function() { | ||
| 83 | + var future = new Future; | ||
| 84 | + var args = Array.prototype.slice.call(arguments); | ||
| 85 | + if (multi) { | ||
| 86 | + var cb = future.resolver(); | ||
| 87 | + args.push(function(err) { | ||
| 88 | + cb(err, Array.prototype.slice.call(arguments, 1)); | ||
| 89 | + }); | ||
| 90 | + } else { | ||
| 91 | + args.push(future.resolver()); | ||
| 92 | + } | ||
| 93 | + future._ = fnOrObject.apply(this, args); | ||
| 94 | + return future; | ||
| 95 | + } | ||
| 96 | + // Modules like `request` return a function that has more functions as properties. Handle this | ||
| 97 | + // in some kind of reasonable way. | ||
| 98 | + if (!stop) { | ||
| 99 | + var proto = Object.create(fnOrObject); | ||
| 100 | + for (var ii in fnOrObject) { | ||
| 101 | + if (fnOrObject.hasOwnProperty(ii) && fnOrObject[ii] instanceof Function) { | ||
| 102 | + proto[ii] = proto[ii]; | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + fn.__proto__ = Future.wrap(proto, multi, suffix, true); | ||
| 106 | + } | ||
| 107 | + return fn; | ||
| 108 | + } | ||
| 109 | +}; | ||
| 110 | + | ||
| 111 | +/** | ||
| 112 | + * Wait on a series of futures and then return. If the futures throw an exception this function | ||
| 113 | + * /won't/ throw it back. You can get the value of the future by calling get() on it directly. If | ||
| 114 | + * you want to wait on a single future you're better off calling future.wait() on the instance. | ||
| 115 | + */ | ||
| 116 | +Future.wait = function wait(/* ... */) { | ||
| 117 | + | ||
| 118 | + // Normalize arguments + pull out a FiberFuture for reuse if possible | ||
| 119 | + var futures = [], singleFiberFuture; | ||
| 120 | + for (var ii = 0; ii < arguments.length; ++ii) { | ||
| 121 | + var arg = arguments[ii]; | ||
| 122 | + if (arg instanceof Future) { | ||
| 123 | + // Ignore already resolved fibers | ||
| 124 | + if (arg.isResolved()) { | ||
| 125 | + continue; | ||
| 126 | + } | ||
| 127 | + // Look for fiber reuse | ||
| 128 | + if (!singleFiberFuture && arg instanceof FiberFuture && !arg.started) { | ||
| 129 | + singleFiberFuture = arg; | ||
| 130 | + continue; | ||
| 131 | + } | ||
| 132 | + futures.push(arg); | ||
| 133 | + } else if (arg instanceof Array) { | ||
| 134 | + for (var jj = 0; jj < arg.length; ++jj) { | ||
| 135 | + var aarg = arg[jj]; | ||
| 136 | + if (aarg instanceof Future) { | ||
| 137 | + // Ignore already resolved fibers | ||
| 138 | + if (aarg.isResolved()) { | ||
| 139 | + continue; | ||
| 140 | + } | ||
| 141 | + // Look for fiber reuse | ||
| 142 | + if (!singleFiberFuture && aarg instanceof FiberFuture && !aarg.started) { | ||
| 143 | + singleFiberFuture = aarg; | ||
| 144 | + continue; | ||
| 145 | + } | ||
| 146 | + futures.push(aarg); | ||
| 147 | + } else { | ||
| 148 | + throw new Error(aarg+ ' is not a future'); | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + } else { | ||
| 152 | + throw new Error(arg+ ' is not a future'); | ||
| 153 | + } | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + // Resumes current fiber | ||
| 157 | + var fiber = Fiber.current; | ||
| 158 | + if (!fiber) { | ||
| 159 | + throw new Error('Can\'t wait without a fiber'); | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + // Resolve all futures | ||
| 163 | + var pending = futures.length + (singleFiberFuture ? 1 : 0); | ||
| 164 | + function cb() { | ||
| 165 | + if (!--pending) { | ||
| 166 | + fiber.run(); | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | + for (var ii = 0; ii < futures.length; ++ii) { | ||
| 170 | + futures[ii].resolve(cb); | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + // Reusing a fiber? | ||
| 174 | + if (singleFiberFuture) { | ||
| 175 | + singleFiberFuture.started = true; | ||
| 176 | + try { | ||
| 177 | + singleFiberFuture.return( | ||
| 178 | + singleFiberFuture.fn.apply(singleFiberFuture.context, singleFiberFuture.args)); | ||
| 179 | + } catch(e) { | ||
| 180 | + singleFiberFuture.throw(e); | ||
| 181 | + } | ||
| 182 | + --pending; | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + // Yield this fiber | ||
| 186 | + if (pending) { | ||
| 187 | + Fiber.yield(); | ||
| 188 | + } | ||
| 189 | +}; | ||
| 190 | + | ||
| 191 | +/** | ||
| 192 | + * Return a Future that waits on an ES6 Promise. | ||
| 193 | + */ | ||
| 194 | +Future.fromPromise = function(promise) { | ||
| 195 | + var future = new Future; | ||
| 196 | + promise.then(function(val) { | ||
| 197 | + future.return(val); | ||
| 198 | + }, function(err) { | ||
| 199 | + future.throw(err); | ||
| 200 | + }); | ||
| 201 | + return future; | ||
| 202 | +}; | ||
| 203 | + | ||
| 204 | +Future.prototype = { | ||
| 205 | + /** | ||
| 206 | + * Return the value of this future. If the future hasn't resolved yet this will throw an error. | ||
| 207 | + */ | ||
| 208 | + get: function() { | ||
| 209 | + if (!this.resolved) { | ||
| 210 | + throw new Error('Future must resolve before value is ready'); | ||
| 211 | + } else if (this.error) { | ||
| 212 | + // Link the stack traces up | ||
| 213 | + var error = this.error; | ||
| 214 | + var localStack = {}; | ||
| 215 | + Error.captureStackTrace(localStack, Future.prototype.get); | ||
| 216 | + var futureStack = Object.getOwnPropertyDescriptor(error, 'futureStack'); | ||
| 217 | + if (!futureStack) { | ||
| 218 | + futureStack = Object.getOwnPropertyDescriptor(error, 'stack'); | ||
| 219 | + if (futureStack) { | ||
| 220 | + Object.defineProperty(error, 'futureStack', futureStack); | ||
| 221 | + } | ||
| 222 | + } | ||
| 223 | + if (futureStack && futureStack.get) { | ||
| 224 | + Object.defineProperty(error, 'stack', { | ||
| 225 | + get: function() { | ||
| 226 | + var stack = futureStack.get.apply(error); | ||
| 227 | + if (stack) { | ||
| 228 | + stack = stack.split('\n'); | ||
| 229 | + return [stack[0]] | ||
| 230 | + .concat(localStack.stack.split('\n').slice(1)) | ||
| 231 | + .concat(' - - - - -') | ||
| 232 | + .concat(stack.slice(1)) | ||
| 233 | + .join('\n'); | ||
| 234 | + } else { | ||
| 235 | + return localStack.stack; | ||
| 236 | + } | ||
| 237 | + }, | ||
| 238 | + set: function(stack) { | ||
| 239 | + Object.defineProperty(error, 'stack', { | ||
| 240 | + value: stack, | ||
| 241 | + configurable: true, | ||
| 242 | + enumerable: false, | ||
| 243 | + writable: true, | ||
| 244 | + }); | ||
| 245 | + }, | ||
| 246 | + configurable: true, | ||
| 247 | + enumerable: false, | ||
| 248 | + }); | ||
| 249 | + } | ||
| 250 | + throw error; | ||
| 251 | + } else { | ||
| 252 | + return this.value; | ||
| 253 | + } | ||
| 254 | + }, | ||
| 255 | + | ||
| 256 | + /** | ||
| 257 | + * Mark this future as returned. All pending callbacks will be invoked immediately. | ||
| 258 | + */ | ||
| 259 | + "return": function(value) { | ||
| 260 | + if (this.resolved) { | ||
| 261 | + throw new Error('Future resolved more than once'); | ||
| 262 | + } | ||
| 263 | + this.value = value; | ||
| 264 | + this.resolved = true; | ||
| 265 | + | ||
| 266 | + var callbacks = this.callbacks; | ||
| 267 | + if (callbacks) { | ||
| 268 | + delete this.callbacks; | ||
| 269 | + for (var ii = 0; ii < callbacks.length; ++ii) { | ||
| 270 | + try { | ||
| 271 | + var ref = callbacks[ii]; | ||
| 272 | + if (ref[1]) { | ||
| 273 | + ref[1](value); | ||
| 274 | + } else { | ||
| 275 | + ref[0](undefined, value); | ||
| 276 | + } | ||
| 277 | + } catch(ex) { | ||
| 278 | + // console.log('Resolve cb threw', String(ex.stack || ex.message || ex)); | ||
| 279 | + process.nextTick(function() { | ||
| 280 | + throw(ex); | ||
| 281 | + }); | ||
| 282 | + } | ||
| 283 | + } | ||
| 284 | + } | ||
| 285 | + }, | ||
| 286 | + | ||
| 287 | + /** | ||
| 288 | + * Throw from this future as returned. All pending callbacks will be invoked immediately. | ||
| 289 | + */ | ||
| 290 | + "throw": function(error) { | ||
| 291 | + if (this.resolved) { | ||
| 292 | + throw new Error('Future resolved more than once'); | ||
| 293 | + } else if (!error) { | ||
| 294 | + throw new Error('Must throw non-empty error'); | ||
| 295 | + } | ||
| 296 | + this.error = error; | ||
| 297 | + this.resolved = true; | ||
| 298 | + | ||
| 299 | + var callbacks = this.callbacks; | ||
| 300 | + if (callbacks) { | ||
| 301 | + delete this.callbacks; | ||
| 302 | + for (var ii = 0; ii < callbacks.length; ++ii) { | ||
| 303 | + try { | ||
| 304 | + var ref = callbacks[ii]; | ||
| 305 | + if (ref[1]) { | ||
| 306 | + ref[0].throw(error); | ||
| 307 | + } else { | ||
| 308 | + ref[0](error); | ||
| 309 | + } | ||
| 310 | + } catch(ex) { | ||
| 311 | + // console.log('Resolve cb threw', String(ex.stack || ex.message || ex)); | ||
| 312 | + process.nextTick(function() { | ||
| 313 | + throw(ex); | ||
| 314 | + }); | ||
| 315 | + } | ||
| 316 | + } | ||
| 317 | + } | ||
| 318 | + }, | ||
| 319 | + | ||
| 320 | + /** | ||
| 321 | + * "detach" this future. Basically this is useful if you want to run a task in a future, you | ||
| 322 | + * aren't interested in its return value, but if it throws you don't want the exception to be | ||
| 323 | + * lost. If this fiber throws, an exception will be thrown to the event loop and node will | ||
| 324 | + * probably fall down. | ||
| 325 | + */ | ||
| 326 | + detach: function() { | ||
| 327 | + this.resolve(function(err) { | ||
| 328 | + if (err) { | ||
| 329 | + throw err; | ||
| 330 | + } | ||
| 331 | + }); | ||
| 332 | + }, | ||
| 333 | + | ||
| 334 | + /** | ||
| 335 | + * Returns whether or not this future has resolved yet. | ||
| 336 | + */ | ||
| 337 | + isResolved: function() { | ||
| 338 | + return this.resolved === true; | ||
| 339 | + }, | ||
| 340 | + | ||
| 341 | + /** | ||
| 342 | + * Returns a node-style function which will mark this future as resolved when called. | ||
| 343 | + */ | ||
| 344 | + resolver: function() { | ||
| 345 | + return function(err, val) { | ||
| 346 | + if (err) { | ||
| 347 | + this.throw(err); | ||
| 348 | + } else { | ||
| 349 | + this.return(val); | ||
| 350 | + } | ||
| 351 | + }.bind(this); | ||
| 352 | + }, | ||
| 353 | + | ||
| 354 | + /** | ||
| 355 | + * Waits for this future to resolve and then invokes a callback. | ||
| 356 | + * | ||
| 357 | + * If two arguments are passed, the first argument is a future which will be thrown to in the case | ||
| 358 | + * of error, and the second is a function(val){} callback. | ||
| 359 | + * | ||
| 360 | + * If only one argument is passed it is a standard function(err, val){} callback. | ||
| 361 | + */ | ||
| 362 | + resolve: function(arg1, arg2) { | ||
| 363 | + if (this.resolved) { | ||
| 364 | + if (arg2) { | ||
| 365 | + if (this.error) { | ||
| 366 | + arg1.throw(this.error); | ||
| 367 | + } else { | ||
| 368 | + arg2(this.value); | ||
| 369 | + } | ||
| 370 | + } else { | ||
| 371 | + arg1(this.error, this.value); | ||
| 372 | + } | ||
| 373 | + } else { | ||
| 374 | + (this.callbacks = this.callbacks || []).push([arg1, arg2]); | ||
| 375 | + } | ||
| 376 | + return this; | ||
| 377 | + }, | ||
| 378 | + | ||
| 379 | + /** | ||
| 380 | + * Resolve only in the case of success | ||
| 381 | + */ | ||
| 382 | + resolveSuccess: function(cb) { | ||
| 383 | + this.resolve(function(err, val) { | ||
| 384 | + if (err) { | ||
| 385 | + return; | ||
| 386 | + } | ||
| 387 | + cb(val); | ||
| 388 | + }); | ||
| 389 | + return this; | ||
| 390 | + }, | ||
| 391 | + | ||
| 392 | + /** | ||
| 393 | + * Propogate results to another future. | ||
| 394 | + */ | ||
| 395 | + proxy: function(future) { | ||
| 396 | + this.resolve(function(err, val) { | ||
| 397 | + if (err) { | ||
| 398 | + future.throw(err); | ||
| 399 | + } else { | ||
| 400 | + future.return(val); | ||
| 401 | + } | ||
| 402 | + }); | ||
| 403 | + }, | ||
| 404 | + | ||
| 405 | + /** | ||
| 406 | + * Propogate only errors to an another future or array of futures. | ||
| 407 | + */ | ||
| 408 | + proxyErrors: function(futures) { | ||
| 409 | + this.resolve(function(err) { | ||
| 410 | + if (!err) { | ||
| 411 | + return; | ||
| 412 | + } | ||
| 413 | + if (futures instanceof Array) { | ||
| 414 | + for (var ii = 0; ii < futures.length; ++ii) { | ||
| 415 | + futures[ii].throw(err); | ||
| 416 | + } | ||
| 417 | + } else { | ||
| 418 | + futures.throw(err); | ||
| 419 | + } | ||
| 420 | + }); | ||
| 421 | + return this; | ||
| 422 | + }, | ||
| 423 | + | ||
| 424 | + /** | ||
| 425 | + * Returns an ES6 Promise | ||
| 426 | + */ | ||
| 427 | + promise: function() { | ||
| 428 | + var that = this; | ||
| 429 | + return new Promise(function(resolve, reject) { | ||
| 430 | + that.resolve(function(err, val) { | ||
| 431 | + if (err) { | ||
| 432 | + reject(err); | ||
| 433 | + } else { | ||
| 434 | + resolve(val); | ||
| 435 | + } | ||
| 436 | + }); | ||
| 437 | + }); | ||
| 438 | + }, | ||
| 439 | + | ||
| 440 | + /** | ||
| 441 | + * Differs from its functional counterpart in that it actually resolves the future. Thus if the | ||
| 442 | + * future threw, future.wait() will throw. | ||
| 443 | + */ | ||
| 444 | + wait: function() { | ||
| 445 | + if (this.isResolved()) { | ||
| 446 | + return this.get(); | ||
| 447 | + } | ||
| 448 | + Future.wait(this); | ||
| 449 | + return this.get(); | ||
| 450 | + }, | ||
| 451 | +}; | ||
| 452 | + | ||
| 453 | +/** | ||
| 454 | + * A function call which loads inside a fiber automatically and returns a future. | ||
| 455 | + */ | ||
| 456 | +function FiberFuture(fn, context, args) { | ||
| 457 | + this.fn = fn; | ||
| 458 | + this.context = context; | ||
| 459 | + this.args = args; | ||
| 460 | + this.started = false; | ||
| 461 | + var that = this; | ||
| 462 | + process.nextTick(function() { | ||
| 463 | + if (!that.started) { | ||
| 464 | + that.started = true; | ||
| 465 | + Fiber(function() { | ||
| 466 | + try { | ||
| 467 | + that.return(fn.apply(context, args)); | ||
| 468 | + } catch(e) { | ||
| 469 | + that.throw(e); | ||
| 470 | + } | ||
| 471 | + }).run(); | ||
| 472 | + } | ||
| 473 | + }); | ||
| 474 | +} | ||
| 475 | +util.inherits(FiberFuture, Future); |
| 1 | +{ | ||
| 2 | + "name": "fibers", | ||
| 3 | + "version": "1.0.15", | ||
| 4 | + "description": "Cooperative multi-tasking for Javascript", | ||
| 5 | + "keywords": [ | ||
| 6 | + "fiber", | ||
| 7 | + "fibers", | ||
| 8 | + "coroutine", | ||
| 9 | + "thread", | ||
| 10 | + "async", | ||
| 11 | + "parallel", | ||
| 12 | + "worker", | ||
| 13 | + "future", | ||
| 14 | + "promise" | ||
| 15 | + ], | ||
| 16 | + "homepage": "https://github.com/laverdet/node-fibers", | ||
| 17 | + "author": "Marcel Laverdet <marcel@laverdet.com> (https://github.com/laverdet/)", | ||
| 18 | + "main": "fibers", | ||
| 19 | + "scripts": { | ||
| 20 | + "install": "node build.js || nodejs build.js", | ||
| 21 | + "test": "node test.js || nodejs test.js" | ||
| 22 | + }, | ||
| 23 | + "repository": { | ||
| 24 | + "type": "git", | ||
| 25 | + "url": "git://github.com/laverdet/node-fibers.git" | ||
| 26 | + }, | ||
| 27 | + "license": "MIT", | ||
| 28 | + "engines": { | ||
| 29 | + "node": ">=0.5.2" | ||
| 30 | + }, | ||
| 31 | + "_from": "fibers@1.0.15", | ||
| 32 | + "_resolved": "http://registry.npm.taobao.org/fibers/download/fibers-1.0.15.tgz" | ||
| 33 | +} |
| 1 | +#include "coroutine.h" | ||
| 2 | +#include "v8-version.h" | ||
| 3 | +#include <assert.h> | ||
| 4 | +#ifndef WINDOWS | ||
| 5 | +#include <pthread.h> | ||
| 6 | +#else | ||
| 7 | +#include <windows.h> | ||
| 8 | +#include <intrin.h> | ||
| 9 | +// Stub pthreads into Windows approximations | ||
| 10 | +#define pthread_t HANDLE | ||
| 11 | +#define pthread_create(thread, attr, fn, arg) !((*thread)=CreateThread(NULL, 0, &(fn), arg, 0, NULL)) | ||
| 12 | +#define pthread_join(thread, arg) WaitForSingleObject((thread), INFINITE) | ||
| 13 | +#define pthread_key_t DWORD | ||
| 14 | +#define pthread_key_create(key, dtor) (*key)=TlsAlloc() | ||
| 15 | +#define pthread_setspecific(key, val) TlsSetValue((key), (val)) | ||
| 16 | +#define pthread_getspecific(key) TlsGetValue((key)) | ||
| 17 | +#endif | ||
| 18 | + | ||
| 19 | +#include <stdexcept> | ||
| 20 | +#include <stack> | ||
| 21 | +#include <vector> | ||
| 22 | +using namespace std; | ||
| 23 | + | ||
| 24 | +const size_t v8_tls_keys = 3; | ||
| 25 | +static std::vector<void*> fls_data_pool; | ||
| 26 | +static pthread_key_t coro_thread_key = 0; | ||
| 27 | +static pthread_key_t isolate_key = 0x7777; | ||
| 28 | +static pthread_key_t thread_id_key = 0x7777; | ||
| 29 | +static pthread_key_t thread_data_key = 0x7777; | ||
| 30 | + | ||
| 31 | +static size_t stack_size = 0; | ||
| 32 | +static size_t coroutines_created_ = 0; | ||
| 33 | +static vector<Coroutine*> fiber_pool; | ||
| 34 | +static Coroutine* delete_me = NULL; | ||
| 35 | +size_t Coroutine::pool_size = 120; | ||
| 36 | + | ||
| 37 | +static bool can_poke(void* addr) { | ||
| 38 | +#ifdef WINDOWS | ||
| 39 | + MEMORY_BASIC_INFORMATION mbi; | ||
| 40 | + if (!VirtualQueryEx(GetCurrentProcess(), addr, &mbi, sizeof(mbi))) { | ||
| 41 | + return false; | ||
| 42 | + } | ||
| 43 | + if (!(mbi.State & MEM_COMMIT)) { | ||
| 44 | + return false; | ||
| 45 | + } | ||
| 46 | + return true; | ||
| 47 | +#else | ||
| 48 | + // TODO: Check pointer on other OS's? Windows is the only case I've seen so far that has | ||
| 49 | + // spooky gaps in the TLS key space | ||
| 50 | + return true; | ||
| 51 | +#endif | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +#ifdef USE_V8_SYMBOLS | ||
| 55 | +// Some distributions of node, most notably Ubuntu, strip the v8 internal symbols and so we don't | ||
| 56 | +// have access to this stuff. In most cases we will use the more complicated `find_thread_id_key` | ||
| 57 | +// below, since it tends to work on more platforms. | ||
| 58 | +namespace v8 { | ||
| 59 | + namespace base { | ||
| 60 | + class Thread { | ||
| 61 | + public: typedef int32_t LocalStorageKey; | ||
| 62 | + }; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + namespace internal { | ||
| 66 | + class Isolate { | ||
| 67 | + public: | ||
| 68 | + static base::Thread::LocalStorageKey isolate_key_; | ||
| 69 | + static base::Thread::LocalStorageKey per_isolate_thread_data_key_; | ||
| 70 | + static base::Thread::LocalStorageKey thread_id_key_; | ||
| 71 | + }; | ||
| 72 | + } | ||
| 73 | +} | ||
| 74 | +#endif | ||
| 75 | + | ||
| 76 | +#ifndef WINDOWS | ||
| 77 | +static void* find_thread_id_key(void* arg) | ||
| 78 | +#else | ||
| 79 | +static DWORD __stdcall find_thread_id_key(LPVOID arg) | ||
| 80 | +#endif | ||
| 81 | +{ | ||
| 82 | + v8::Isolate* isolate = static_cast<v8::Isolate*>(arg); | ||
| 83 | + assert(isolate != NULL); | ||
| 84 | + v8::Locker locker(isolate); | ||
| 85 | + isolate->Enter(); | ||
| 86 | + | ||
| 87 | + // First pass-- find isolate thread key | ||
| 88 | + for (pthread_key_t ii = coro_thread_key; ii > 0; --ii) { | ||
| 89 | + void* tls = pthread_getspecific(ii - 1); | ||
| 90 | + if (tls == isolate) { | ||
| 91 | + isolate_key = ii - 1; | ||
| 92 | + break; | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + assert(isolate_key != 0x7777); | ||
| 96 | + | ||
| 97 | + // Second pass-- find data key | ||
| 98 | + int thread_id = 0; | ||
| 99 | + for (pthread_key_t ii = isolate_key + 2; ii < coro_thread_key; ++ii) { | ||
| 100 | + void* tls = pthread_getspecific(ii); | ||
| 101 | + if (can_poke(tls) && *(void**)tls == isolate) { | ||
| 102 | + // First member of per-thread data is the isolate | ||
| 103 | + thread_data_key = ii; | ||
| 104 | + // Second member is the thread id | ||
| 105 | + thread_id = *(int*)((void**)tls + 1); | ||
| 106 | + break; | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + assert(thread_data_key != 0x7777); | ||
| 110 | + | ||
| 111 | + // Third pass-- find thread id key | ||
| 112 | + for (pthread_key_t ii = isolate_key + 1; ii < thread_data_key; ++ii) { | ||
| 113 | + int tls = static_cast<int>(reinterpret_cast<intptr_t>(pthread_getspecific(ii))); | ||
| 114 | + if (tls == thread_id) { | ||
| 115 | + thread_id_key = ii; | ||
| 116 | + break; | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + assert(thread_id_key != 0x7777); | ||
| 120 | + | ||
| 121 | + isolate->Exit(); | ||
| 122 | + return NULL; | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +/** | ||
| 126 | + * Coroutine class definition | ||
| 127 | + */ | ||
| 128 | +void Coroutine::init(v8::Isolate* isolate) { | ||
| 129 | + v8::Unlocker unlocker(isolate); | ||
| 130 | + pthread_key_create(&coro_thread_key, NULL); | ||
| 131 | + pthread_setspecific(coro_thread_key, ¤t()); | ||
| 132 | +#ifdef USE_V8_SYMBOLS | ||
| 133 | + isolate_key = v8::internal::Isolate::isolate_key_; | ||
| 134 | + thread_data_key = v8::internal::Isolate::per_isolate_thread_data_key_; | ||
| 135 | + thread_id_key = v8::internal::Isolate::thread_id_key_; | ||
| 136 | +#else | ||
| 137 | + pthread_t thread; | ||
| 138 | + pthread_create(&thread, NULL, find_thread_id_key, isolate); | ||
| 139 | + pthread_join(thread, NULL); | ||
| 140 | +#endif | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +Coroutine& Coroutine::current() { | ||
| 144 | + Coroutine* current = static_cast<Coroutine*>(pthread_getspecific(coro_thread_key)); | ||
| 145 | + if (!current) { | ||
| 146 | + current = new Coroutine; | ||
| 147 | + pthread_setspecific(coro_thread_key, current); | ||
| 148 | + } | ||
| 149 | + return *current; | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +void Coroutine::set_stack_size(unsigned int size) { | ||
| 153 | + assert(!stack_size); | ||
| 154 | + stack_size = size; | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +size_t Coroutine::coroutines_created() { | ||
| 158 | + return coroutines_created_; | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +void Coroutine::trampoline(void* that) { | ||
| 162 | +#ifdef CORO_PTHREAD | ||
| 163 | + pthread_setspecific(coro_thread_key, that); | ||
| 164 | +#endif | ||
| 165 | +#ifdef CORO_FIBER | ||
| 166 | + // I can't figure out how to get the precise base of the stack in Windows. Since CreateFiber | ||
| 167 | + // creates the stack automatically we don't have access to the base. We can however grab the | ||
| 168 | + // current esp position, and use that as an approximation. Padding is added for safety since the | ||
| 169 | + // base is slightly different. | ||
| 170 | + static_cast<Coroutine*>(that)->stack_base = (size_t*)_AddressOfReturnAddress() - stack_size + 16; | ||
| 171 | +#endif | ||
| 172 | + if (!fls_data_pool.empty()) { | ||
| 173 | + pthread_setspecific(thread_data_key, fls_data_pool.back()); | ||
| 174 | + pthread_setspecific(thread_id_key, fls_data_pool.at(fls_data_pool.size() - 2)); | ||
| 175 | + pthread_setspecific(isolate_key, fls_data_pool.at(fls_data_pool.size() - 3)); | ||
| 176 | + fls_data_pool.resize(fls_data_pool.size() - 3); | ||
| 177 | + } | ||
| 178 | + while (true) { | ||
| 179 | + static_cast<Coroutine*>(that)->entry(const_cast<void*>(static_cast<Coroutine*>(that)->arg)); | ||
| 180 | + } | ||
| 181 | +} | ||
| 182 | + | ||
| 183 | +Coroutine::Coroutine() : | ||
| 184 | + fls_data(v8_tls_keys), | ||
| 185 | + entry(NULL), | ||
| 186 | + arg(NULL) { | ||
| 187 | + stack.sptr = NULL; | ||
| 188 | + coro_create(&context, NULL, NULL, NULL, 0); | ||
| 189 | +} | ||
| 190 | + | ||
| 191 | +Coroutine::Coroutine(entry_t& entry, void* arg) : | ||
| 192 | + fls_data(v8_tls_keys), | ||
| 193 | + entry(entry), | ||
| 194 | + arg(arg) { | ||
| 195 | +} | ||
| 196 | + | ||
| 197 | +Coroutine::~Coroutine() { | ||
| 198 | + if (stack.sptr) { | ||
| 199 | + coro_stack_free(&stack); | ||
| 200 | + } | ||
| 201 | +#ifdef CORO_FIBER | ||
| 202 | + if (context.fiber) | ||
| 203 | +#endif | ||
| 204 | + (void)coro_destroy(&context); | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +Coroutine* Coroutine::create_fiber(entry_t* entry, void* arg) { | ||
| 208 | + if (!fiber_pool.empty()) { | ||
| 209 | + Coroutine* fiber = fiber_pool.back(); | ||
| 210 | + fiber_pool.pop_back(); | ||
| 211 | + fiber->reset(entry, arg); | ||
| 212 | + return fiber; | ||
| 213 | + } | ||
| 214 | + Coroutine* coro = new Coroutine(*entry, arg); | ||
| 215 | + if (!coro_stack_alloc(&coro->stack, stack_size)) { | ||
| 216 | + delete coro; | ||
| 217 | + return NULL; | ||
| 218 | + } | ||
| 219 | + coro_create(&coro->context, trampoline, coro, coro->stack.sptr, coro->stack.ssze); | ||
| 220 | +#ifdef CORO_FIBER | ||
| 221 | + // Stupid hack. libcoro's project structure combined with Windows's CreateFiber functions makes | ||
| 222 | + // it difficult to catch this error. Sometimes Windows will return `ERROR_NOT_ENOUGH_MEMORY` or | ||
| 223 | + // `ERROR_COMMITMENT_LIMIT` if it can't make any more fibers. However, `coro_stack_alloc` returns | ||
| 224 | + // success unconditionally on Windows so we have to detect the error here, after the call to | ||
| 225 | + // `coro_create`. | ||
| 226 | + if (!coro->context.fiber) { | ||
| 227 | + delete coro; | ||
| 228 | + return NULL; | ||
| 229 | + } | ||
| 230 | +#endif | ||
| 231 | + ++coroutines_created_; | ||
| 232 | + return coro; | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +void Coroutine::reset(entry_t* entry, void* arg) { | ||
| 236 | + assert(entry != NULL); | ||
| 237 | + this->entry = entry; | ||
| 238 | + this->arg = arg; | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +void Coroutine::transfer(Coroutine& next) { | ||
| 242 | + assert(this != &next); | ||
| 243 | +#ifndef CORO_PTHREAD | ||
| 244 | + fls_data[0] = pthread_getspecific(isolate_key); | ||
| 245 | + fls_data[1] = pthread_getspecific(thread_id_key); | ||
| 246 | + fls_data[2] = pthread_getspecific(thread_data_key); | ||
| 247 | + | ||
| 248 | + pthread_setspecific(isolate_key, next.fls_data[0]); | ||
| 249 | + pthread_setspecific(thread_id_key, next.fls_data[1]); | ||
| 250 | + pthread_setspecific(thread_data_key, next.fls_data[2]); | ||
| 251 | + | ||
| 252 | + pthread_setspecific(coro_thread_key, &next); | ||
| 253 | +#endif | ||
| 254 | + coro_transfer(&context, &next.context); | ||
| 255 | +#ifndef CORO_PTHREAD | ||
| 256 | + pthread_setspecific(coro_thread_key, this); | ||
| 257 | +#endif | ||
| 258 | +} | ||
| 259 | + | ||
| 260 | +void Coroutine::run() { | ||
| 261 | + Coroutine& current = Coroutine::current(); | ||
| 262 | + assert(!delete_me); | ||
| 263 | + assert(¤t != this); | ||
| 264 | + current.transfer(*this); | ||
| 265 | + | ||
| 266 | + if (delete_me) { | ||
| 267 | + // This means finish() was called on the coroutine and the pool was full so this coroutine needs | ||
| 268 | + // to be deleted. We can't delete from inside finish(), because that would deallocate the | ||
| 269 | + // current stack. However we CAN delete here, we just have to be very careful. | ||
| 270 | + assert(delete_me == this); | ||
| 271 | + assert(¤t != this); | ||
| 272 | + delete_me = NULL; | ||
| 273 | + delete this; | ||
| 274 | + } | ||
| 275 | +} | ||
| 276 | + | ||
| 277 | +void Coroutine::finish(Coroutine& next, v8::Isolate* isolate) { | ||
| 278 | + { | ||
| 279 | + assert(&next != this); | ||
| 280 | + assert(¤t() == this); | ||
| 281 | + if (fiber_pool.size() < pool_size) { | ||
| 282 | + fiber_pool.push_back(this); | ||
| 283 | + } else { | ||
| 284 | +#if V8_MAJOR_VERSION > 4 || (V8_MAJOR_VERSION == 4 && V8_MINOR_VERSION >= 10) | ||
| 285 | + // Clean up isolate data | ||
| 286 | + isolate->DiscardThreadSpecificMetadata(); | ||
| 287 | +#else | ||
| 288 | + // If not supported, then we can mitigate v8's leakage by saving these thread locals. | ||
| 289 | + fls_data_pool.reserve(fls_data_pool.size() + 3); | ||
| 290 | + fls_data_pool.push_back(pthread_getspecific(isolate_key)); | ||
| 291 | + fls_data_pool.push_back(pthread_getspecific(thread_id_key)); | ||
| 292 | + fls_data_pool.push_back(pthread_getspecific(thread_data_key)); | ||
| 293 | +#endif | ||
| 294 | + // Can't delete right now because we're currently on this stack! | ||
| 295 | + assert(delete_me == NULL); | ||
| 296 | + delete_me = this; | ||
| 297 | + } | ||
| 298 | + } | ||
| 299 | + this->transfer(next); | ||
| 300 | +} | ||
| 301 | + | ||
| 302 | +void* Coroutine::bottom() const { | ||
| 303 | +#ifdef CORO_FIBER | ||
| 304 | + return stack_base; | ||
| 305 | +#else | ||
| 306 | + return stack.sptr; | ||
| 307 | +#endif | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | +size_t Coroutine::size() const { | ||
| 311 | + return sizeof(Coroutine) + stack_size * sizeof(void*); | ||
| 312 | +} |
| 1 | +#include <node.h> | ||
| 2 | +#include <stdlib.h> | ||
| 3 | +#include <vector> | ||
| 4 | +#include "libcoro/coro.h" | ||
| 5 | + | ||
| 6 | +class Coroutine { | ||
| 7 | + public: | ||
| 8 | + typedef void(entry_t)(void*); | ||
| 9 | + | ||
| 10 | + private: | ||
| 11 | +#ifdef CORO_FIBER | ||
| 12 | + void* stack_base; | ||
| 13 | +#endif | ||
| 14 | + coro_context context; | ||
| 15 | + coro_stack stack; | ||
| 16 | + std::vector<void*> fls_data; | ||
| 17 | + entry_t* entry; | ||
| 18 | + void* arg; | ||
| 19 | + | ||
| 20 | + ~Coroutine(); | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * Constructor for currently running "fiber". This is really just original thread, but we | ||
| 24 | + * need a way to get back into the main thread after yielding to a fiber. Basically this | ||
| 25 | + * shouldn't be called from anywhere. | ||
| 26 | + */ | ||
| 27 | + Coroutine(); | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * This constructor will actually create a new fiber context. Execution does not begin | ||
| 31 | + * until you call run() for the first time. | ||
| 32 | + */ | ||
| 33 | + Coroutine(entry_t& entry, void* arg); | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * Resets the context of this coroutine from the start. Used to recyle old coroutines. | ||
| 37 | + */ | ||
| 38 | + void reset(entry_t* entry, void* arg); | ||
| 39 | + | ||
| 40 | + static void trampoline(void* that); | ||
| 41 | + void transfer(Coroutine& next); | ||
| 42 | + | ||
| 43 | + public: | ||
| 44 | + static size_t pool_size; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * Returns the currently-running fiber. | ||
| 48 | + */ | ||
| 49 | + static Coroutine& current(); | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * Create a new fiber. | ||
| 53 | + */ | ||
| 54 | + static Coroutine* create_fiber(entry_t* entry, void* arg = NULL); | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * Initialize the library. | ||
| 58 | + */ | ||
| 59 | + static void init(v8::Isolate* isolate); | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * Set the size of coroutines created by this library. Since coroutines are pooled the stack | ||
| 63 | + * size is global instead of per-coroutine. Stack is measured in sizeof(void*), so | ||
| 64 | + * set_stack_size(128) -> 512 bytes or 1kb | ||
| 65 | + */ | ||
| 66 | + static void set_stack_size(unsigned int size); | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * Get the number of coroutines that have been created. | ||
| 70 | + */ | ||
| 71 | + static size_t coroutines_created(); | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * Start or resume execution in this fiber. Note there is no explicit yield() function, | ||
| 75 | + * you must manually run another fiber. | ||
| 76 | + */ | ||
| 77 | + void run(); | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * Finish this coroutine.. This will halt execution of this coroutine and resume execution | ||
| 81 | + * of `next`. If you do not call this function, and instead just return from `entry` the | ||
| 82 | + * application will exit. This function may or may not actually return. | ||
| 83 | + */ | ||
| 84 | + void finish(Coroutine& next, v8::Isolate* isolate); | ||
| 85 | + | ||
| 86 | + /** | ||
| 87 | + * Returns address of the lowest usable byte in this Coroutine's stack. | ||
| 88 | + */ | ||
| 89 | + void* bottom() const; | ||
| 90 | + | ||
| 91 | + /** | ||
| 92 | + * Returns the size this Coroutine takes up in the heap. | ||
| 93 | + */ | ||
| 94 | + size_t size() const; | ||
| 95 | +}; |
| 1 | +#include "coroutine.h" | ||
| 2 | +#include "v8-version.h" | ||
| 3 | +#include <assert.h> | ||
| 4 | +#include <node.h> | ||
| 5 | +#include <node_version.h> | ||
| 6 | + | ||
| 7 | +#include <vector> | ||
| 8 | +#include <iostream> | ||
| 9 | + | ||
| 10 | +#define THROW(x, m) return uni::Return(uni::ThrowException(Isolate::GetCurrent(), x(uni::NewLatin1String(Isolate::GetCurrent(), m))), args) | ||
| 11 | + | ||
| 12 | +// Run GC more often when debugging | ||
| 13 | +#ifdef DEBUG | ||
| 14 | +#define GC_ADJUST 100 | ||
| 15 | +#else | ||
| 16 | +#define GC_ADJUST 1 | ||
| 17 | +#endif | ||
| 18 | + | ||
| 19 | +using namespace std; | ||
| 20 | +using namespace v8; | ||
| 21 | + | ||
| 22 | +// Handle legacy V8 API | ||
| 23 | +namespace uni { | ||
| 24 | +#if V8_MAJOR_VERSION > 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION >= 2) | ||
| 25 | + // Actually 5.2.244 | ||
| 26 | + template <void (*F)(void*), class P> | ||
| 27 | + void WeakCallbackShim(const WeakCallbackInfo<P>& data) { | ||
| 28 | + F(data.GetParameter()); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + template <void (*F)(void*), class T, typename P> | ||
| 32 | + void MakeWeak(Isolate* isolate, Persistent<T>& handle, P* val) { | ||
| 33 | + handle.SetWeak(val, WeakCallbackShim<F, P>, WeakCallbackType::kFinalizer); | ||
| 34 | + } | ||
| 35 | +#elif V8_MAJOR_VERSION > 3 || (V8_MAJOR_VERSION == 3 && V8_MINOR_VERSION >= 26) | ||
| 36 | + template <void (*F)(void*), class T, typename P> | ||
| 37 | + void WeakCallbackShim(const v8::WeakCallbackData<T, P>& data) { | ||
| 38 | + F(data.GetParameter()); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + template <void (*F)(void*), class T, typename P> | ||
| 42 | + void MakeWeak(Isolate* isolate, Persistent<T>& handle, P* val) { | ||
| 43 | + handle.SetWeak(val, WeakCallbackShim<F>); | ||
| 44 | + } | ||
| 45 | +#else | ||
| 46 | + template <void (*F)(void*)> | ||
| 47 | + void WeakCallbackShim(Persistent<Value> value, void* data) { | ||
| 48 | + F(data); | ||
| 49 | + } | ||
| 50 | + template <void (*F)(void*), class T, typename P> | ||
| 51 | + void MakeWeak(Isolate* isolate, Persistent<T>& handle, P* val) { | ||
| 52 | + handle.MakeWeak(val, WeakCallbackShim<F>); | ||
| 53 | + } | ||
| 54 | +#endif | ||
| 55 | + | ||
| 56 | + | ||
| 57 | +#if V8_MAJOR_VERSION > 3 || (V8_MAJOR_VERSION == 3 && V8_MINOR_VERSION >= 26) | ||
| 58 | + // Node v0.11.13+ | ||
| 59 | + typedef PropertyCallbackInfo<Value> GetterCallbackInfo; | ||
| 60 | + typedef PropertyCallbackInfo<void> SetterCallbackInfo; | ||
| 61 | + typedef void FunctionType; | ||
| 62 | + typedef FunctionCallbackInfo<v8::Value> Arguments; | ||
| 63 | + | ||
| 64 | + class HandleScope { | ||
| 65 | + v8::HandleScope scope; | ||
| 66 | + public: HandleScope(Isolate* isolate) : scope(isolate) {} | ||
| 67 | + }; | ||
| 68 | + | ||
| 69 | + template <class T> | ||
| 70 | + void Reset(Isolate* isolate, Persistent<T>& persistent, Handle<T> handle) { | ||
| 71 | + persistent.Reset(isolate, handle); | ||
| 72 | + } | ||
| 73 | + template <class T> | ||
| 74 | + void Dispose(Isolate* isolate, Persistent<T>& handle) { | ||
| 75 | + handle.Reset(); | ||
| 76 | + } | ||
| 77 | + template <class T> | ||
| 78 | + void ClearWeak(Isolate* isolate, Persistent<T>& handle) { | ||
| 79 | + handle.ClearWeak(isolate); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + template <class T> | ||
| 83 | + void SetInternalPointer(Handle<T> handle, int index, void* val) { | ||
| 84 | + handle->SetAlignedPointerInInternalField(index, val); | ||
| 85 | + } | ||
| 86 | + template <class T> | ||
| 87 | + void* GetInternalPointer(Handle<T> handle, int index) { | ||
| 88 | + return handle->GetAlignedPointerFromInternalField(index); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + template <class T> | ||
| 92 | + Handle<T> Deref(Isolate* isolate, Persistent<T>& handle) { | ||
| 93 | + return Local<T>::New(isolate, handle); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + template <class T> | ||
| 97 | + void Return(Handle<T> handle, const Arguments& args) { | ||
| 98 | + args.GetReturnValue().Set(handle); | ||
| 99 | + } | ||
| 100 | + template <class T> | ||
| 101 | + void Return(Handle<T> handle, GetterCallbackInfo info) { | ||
| 102 | + info.GetReturnValue().Set(handle); | ||
| 103 | + } | ||
| 104 | + template <class T> | ||
| 105 | + void Return(Persistent<T>& handle, GetterCallbackInfo info) { | ||
| 106 | + info.GetReturnValue().Set(handle); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + Handle<Value> ThrowException(Isolate* isolate, Handle<Value> exception) { | ||
| 110 | + return isolate->ThrowException(exception); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + Handle<Context> GetCurrentContext(Isolate* isolate) { | ||
| 114 | + return isolate->GetCurrentContext(); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + Handle<Primitive> Undefined(Isolate* isolate) { | ||
| 118 | + return v8::Undefined(isolate); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + Handle<String> NewLatin1String(Isolate* isolate, const char* string) { | ||
| 122 | + return String::NewFromOneByte(isolate, (const uint8_t*)string); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + Handle<String> NewLatin1Symbol(Isolate* isolate, const char* string) { | ||
| 126 | + return String::NewFromOneByte(isolate, (const uint8_t*)string); | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + Handle<Boolean> NewBoolean(Isolate* isolate, bool value) { | ||
| 130 | + return Boolean::New(isolate, value); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + Handle<Number> NewNumber(Isolate* isolate, double value) { | ||
| 134 | + return Number::New(isolate, value); | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + Handle<FunctionTemplate> NewFunctionTemplate( | ||
| 138 | + Isolate* isolate, | ||
| 139 | + FunctionCallback callback, | ||
| 140 | + Handle<Value> data = Handle<Value>(), | ||
| 141 | + Handle<Signature> signature = Handle<Signature>(), | ||
| 142 | + int length = 0 | ||
| 143 | + ) { | ||
| 144 | + return FunctionTemplate::New(isolate, callback, data, signature, length); | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + Handle<Signature> NewSignature( | ||
| 148 | + Isolate* isolate, | ||
| 149 | + Handle<FunctionTemplate> receiver = Handle<FunctionTemplate>() | ||
| 150 | + ) { | ||
| 151 | + return Signature::New(isolate, receiver); | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + class ReverseIsolateScope { | ||
| 155 | + Isolate* isolate; | ||
| 156 | + public: | ||
| 157 | + explicit inline ReverseIsolateScope(Isolate* isolate) : isolate(isolate) { | ||
| 158 | + isolate->Exit(); | ||
| 159 | + } | ||
| 160 | + inline ~ReverseIsolateScope() { | ||
| 161 | + isolate->Enter(); | ||
| 162 | + } | ||
| 163 | + }; | ||
| 164 | + | ||
| 165 | + void AdjustAmountOfExternalAllocatedMemory(Isolate* isolate, int64_t change_in_bytes) { | ||
| 166 | + isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); | ||
| 167 | + } | ||
| 168 | +#else | ||
| 169 | + // Node v0.10.x and lower | ||
| 170 | + typedef AccessorInfo GetterCallbackInfo; | ||
| 171 | + typedef AccessorInfo SetterCallbackInfo; | ||
| 172 | + typedef Handle<Value> FunctionType; | ||
| 173 | + typedef Arguments Arguments; | ||
| 174 | + | ||
| 175 | + class HandleScope { | ||
| 176 | + v8::HandleScope scope; | ||
| 177 | + public: HandleScope(Isolate* isolate) {} | ||
| 178 | + }; | ||
| 179 | + | ||
| 180 | + template <class T> | ||
| 181 | + void Reset(Isolate* isolate, Persistent<T>& persistent, Handle<T> handle) { | ||
| 182 | + persistent = Persistent<T>::New(handle); | ||
| 183 | + } | ||
| 184 | + template <class T> | ||
| 185 | + void Dispose(Isolate* isolate, Persistent<T>& handle) { | ||
| 186 | + handle.Dispose(); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + template <class T> | ||
| 190 | + void ClearWeak(Isolate* isolate, Persistent<T>& handle) { | ||
| 191 | + handle.ClearWeak(); | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + template <class T> | ||
| 195 | + void SetInternalPointer(Handle<T> handle, int index, void* val) { | ||
| 196 | + handle->SetPointerInInternalField(index, val); | ||
| 197 | + } | ||
| 198 | + template <class T> | ||
| 199 | + void* GetInternalPointer(Handle<T> handle, int index) { | ||
| 200 | + return handle->GetPointerFromInternalField(index); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + template <class T> | ||
| 204 | + Handle<T> Deref(Isolate* isolate, Persistent<T>& handle) { | ||
| 205 | + return Local<T>::New(handle); | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + Handle<Value> Return(Handle<Value> handle, GetterCallbackInfo info) { | ||
| 209 | + return handle; | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + Handle<Value> Return(Handle<Value> handle, const Arguments& args) { | ||
| 213 | + return handle; | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + Handle<Value> ThrowException(Isolate* isolate, Handle<Value> exception) { | ||
| 217 | + return ThrowException(exception); | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + Handle<Context> GetCurrentContext(Isolate* isolate) { | ||
| 221 | + return Context::GetCurrent(); | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + Handle<Primitive> Undefined(Isolate* isolate) { | ||
| 225 | + return v8::Undefined(); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + Handle<String> NewLatin1String(Isolate* isolate, const char* string) { | ||
| 229 | + return String::New(string); | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + Handle<String> NewLatin1Symbol(Isolate* isolate, const char* string) { | ||
| 233 | + return String::NewSymbol(string); | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + Handle<Boolean> NewBoolean(Isolate* isolate, bool value) { | ||
| 237 | + return Boolean::New(value); | ||
| 238 | + } | ||
| 239 | + | ||
| 240 | + Handle<Number> NewNumber(Isolate* isolate, double value) { | ||
| 241 | + return Number::New(value); | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + Handle<FunctionTemplate> NewFunctionTemplate( | ||
| 245 | + Isolate* isolate, | ||
| 246 | + InvocationCallback callback, | ||
| 247 | + Handle<Value> data = Handle<Value>(), | ||
| 248 | + Handle<Signature> signature = Handle<Signature>(), | ||
| 249 | + int length = 0 | ||
| 250 | + ) { | ||
| 251 | + return FunctionTemplate::New(callback, data, signature); | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | + Handle<Signature> NewSignature( | ||
| 255 | + Isolate* isolate, | ||
| 256 | + Handle<FunctionTemplate> receiver = Handle<FunctionTemplate>(), | ||
| 257 | + int argc = 0, | ||
| 258 | + Handle<FunctionTemplate> argv[] = 0 | ||
| 259 | + ) { | ||
| 260 | + return Signature::New(receiver, argc, argv); | ||
| 261 | + } | ||
| 262 | + | ||
| 263 | + class ReverseIsolateScope { | ||
| 264 | + public: explicit inline ReverseIsolateScope(Isolate* isolate) {} | ||
| 265 | + }; | ||
| 266 | + | ||
| 267 | + void AdjustAmountOfExternalAllocatedMemory(Isolate* isolate, int64_t change_in_bytes) { | ||
| 268 | + V8::AdjustAmountOfExternalAllocatedMemory(change_in_bytes); | ||
| 269 | + } | ||
| 270 | +#endif | ||
| 271 | + | ||
| 272 | +#if V8_MAJOR_VERSION > 3 || (V8_MAJOR_VERSION == 3 && V8_MINOR_VERSION >= 29) | ||
| 273 | + // This was actually added in 3.29.67 | ||
| 274 | + void SetStackGuard(Isolate* isolate, void* guard) { | ||
| 275 | + isolate->SetStackLimit(reinterpret_cast<uintptr_t>(guard)); | ||
| 276 | + } | ||
| 277 | +#elif V8_MAJOR_VERSION > 3 || (V8_MAJOR_VERSION == 3 && V8_MINOR_VERSION >= 26) | ||
| 278 | + void SetStackGuard(Isolate* isolate, void* guard) { | ||
| 279 | + ResourceConstraints constraints; | ||
| 280 | + constraints.set_stack_limit(reinterpret_cast<uint32_t*>(guard)); | ||
| 281 | + v8::SetResourceConstraints(isolate, &constraints); | ||
| 282 | + } | ||
| 283 | +#else | ||
| 284 | + // Extra padding for old versions of v8. Shit's fucked. | ||
| 285 | + void SetStackGuard(Isolate* isolate, void* guard) { | ||
| 286 | + ResourceConstraints constraints; | ||
| 287 | + constraints.set_stack_limit( | ||
| 288 | + reinterpret_cast<uint32_t*>(guard) + 18 * 1024 | ||
| 289 | + ); | ||
| 290 | + v8::SetResourceConstraints(&constraints); | ||
| 291 | + } | ||
| 292 | +#endif | ||
| 293 | +} | ||
| 294 | + | ||
| 295 | +class Fiber { | ||
| 296 | + | ||
| 297 | + private: | ||
| 298 | + static Locker* global_locker; // Node does not use locks or threads, so we need a global lock | ||
| 299 | + static Persistent<FunctionTemplate> tmpl; | ||
| 300 | + static Persistent<Function> fiber_object; | ||
| 301 | + static Fiber* current; | ||
| 302 | + static vector<Fiber*> orphaned_fibers; | ||
| 303 | + static Persistent<Value> fatal_stack; | ||
| 304 | + | ||
| 305 | + Isolate* isolate; | ||
| 306 | + Persistent<Object> handle; | ||
| 307 | + Persistent<Function> cb; | ||
| 308 | + Persistent<Context> v8_context; | ||
| 309 | + Persistent<Value> zombie_exception; | ||
| 310 | + Persistent<Value> yielded; | ||
| 311 | + bool yielded_exception; | ||
| 312 | + Coroutine* entry_fiber; | ||
| 313 | + Coroutine* this_fiber; | ||
| 314 | + bool started; | ||
| 315 | + bool yielding; | ||
| 316 | + bool zombie; | ||
| 317 | + bool resetting; | ||
| 318 | + | ||
| 319 | + static Fiber& Unwrap(Handle<Object> handle) { | ||
| 320 | + assert(!handle.IsEmpty()); | ||
| 321 | + assert(handle->InternalFieldCount() == 1); | ||
| 322 | + return *static_cast<Fiber*>(uni::GetInternalPointer(handle, 0)); | ||
| 323 | + } | ||
| 324 | + | ||
| 325 | + Fiber(Handle<Object> handle, Handle<Function> cb, Handle<Context> v8_context) : | ||
| 326 | + isolate(Isolate::GetCurrent()), | ||
| 327 | + started(false), | ||
| 328 | + yielding(false), | ||
| 329 | + zombie(false), | ||
| 330 | + resetting(false) { | ||
| 331 | + uni::Reset(isolate, this->handle, handle); | ||
| 332 | + uni::Reset(isolate, this->cb, cb); | ||
| 333 | + uni::Reset(isolate, this->v8_context, v8_context); | ||
| 334 | + | ||
| 335 | + MakeWeak(); | ||
| 336 | + uni::SetInternalPointer(handle, 0, this); | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | + virtual ~Fiber() { | ||
| 340 | + assert(!this->started); | ||
| 341 | + uni::Dispose(isolate, handle); | ||
| 342 | + uni::Dispose(isolate, cb); | ||
| 343 | + uni::Dispose(isolate, v8_context); | ||
| 344 | + } | ||
| 345 | + | ||
| 346 | + /** | ||
| 347 | + * Call MakeWeak if it's ok for v8 to garbage collect this Fiber. | ||
| 348 | + * i.e. After fiber completes, while yielded, or before started | ||
| 349 | + */ | ||
| 350 | + void MakeWeak() { | ||
| 351 | + uni::MakeWeak<WeakCallback>(isolate, handle, (void*)this); | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + /** | ||
| 355 | + * And call ClearWeak if it's not ok for v8 to garbage collect this Fiber. | ||
| 356 | + * i.e. While running. | ||
| 357 | + */ | ||
| 358 | + void ClearWeak() { | ||
| 359 | + handle.ClearWeak(); | ||
| 360 | + } | ||
| 361 | + | ||
| 362 | + /** | ||
| 363 | + * Called when there are no more references to this object in Javascript. If this happens and | ||
| 364 | + * the fiber is currently suspended we'll unwind the fiber's stack by throwing exceptions in | ||
| 365 | + * order to clear all references. | ||
| 366 | + */ | ||
| 367 | + static void WeakCallback(void* data) { | ||
| 368 | + Fiber& that = *static_cast<Fiber*>(data); | ||
| 369 | + assert(that.handle.IsNearDeath()); | ||
| 370 | + assert(current != &that); | ||
| 371 | + | ||
| 372 | + // We'll unwind running fibers later... doing it from the garbage collector is bad news. | ||
| 373 | + if (that.started) { | ||
| 374 | + assert(that.yielding); | ||
| 375 | + orphaned_fibers.push_back(&that); | ||
| 376 | + that.ClearWeak(); | ||
| 377 | + return; | ||
| 378 | + } | ||
| 379 | + | ||
| 380 | + delete &that; | ||
| 381 | + } | ||
| 382 | + | ||
| 383 | + /** | ||
| 384 | + * When the v8 garbage collector notifies us about dying fibers instead of unwindng their | ||
| 385 | + * stack as soon as possible we put them aside to unwind later. Unwinding from the garbage | ||
| 386 | + * collector leads to exponential time garbage collections if there are many orphaned Fibers, | ||
| 387 | + * there's also the possibility of running out of stack space. It's generally bad news. | ||
| 388 | + * | ||
| 389 | + * So instead we have this function to clean up all the fibers after the garbage collection | ||
| 390 | + * has finished. | ||
| 391 | + */ | ||
| 392 | + static void DestroyOrphans() { | ||
| 393 | + if (orphaned_fibers.empty()) { | ||
| 394 | + return; | ||
| 395 | + } | ||
| 396 | + vector<Fiber*> orphans(orphaned_fibers); | ||
| 397 | + orphaned_fibers.clear(); | ||
| 398 | + | ||
| 399 | + for (vector<Fiber*>::iterator ii = orphans.begin(); ii != orphans.end(); ++ii) { | ||
| 400 | + Fiber& that = **ii; | ||
| 401 | + that.UnwindStack(); | ||
| 402 | + | ||
| 403 | + if (that.yielded_exception) { | ||
| 404 | + // If you throw an exception from a fiber that's being garbage collected there's no way | ||
| 405 | + // to bubble that exception up to the application. | ||
| 406 | + String::Utf8Value stack(uni::Deref(that.isolate, fatal_stack)); | ||
| 407 | + cerr << | ||
| 408 | + "An exception was thrown from a Fiber which was being garbage collected. This error " | ||
| 409 | + "can not be gracefully recovered from. The only acceptable behavior is to terminate " | ||
| 410 | + "this application. The exception appears below:\n\n" | ||
| 411 | + <<*stack <<"\n"; | ||
| 412 | + exit(1); | ||
| 413 | + } else { | ||
| 414 | + uni::Dispose(that.isolate, fatal_stack); | ||
| 415 | + } | ||
| 416 | + | ||
| 417 | + uni::Dispose(that.isolate, that.yielded); | ||
| 418 | + that.MakeWeak(); | ||
| 419 | + } | ||
| 420 | + } | ||
| 421 | + | ||
| 422 | + /** | ||
| 423 | + * Instantiate a new Fiber object. When a fiber is created it only grabs a handle to the | ||
| 424 | + * callback; it doesn't create any new contexts until run() is called. | ||
| 425 | + */ | ||
| 426 | + static uni::FunctionType New(const uni::Arguments& args) { | ||
| 427 | + if (args.Length() != 1) { | ||
| 428 | + THROW(Exception::TypeError, "Fiber expects 1 argument"); | ||
| 429 | + } else if (!args[0]->IsFunction()) { | ||
| 430 | + THROW(Exception::TypeError, "Fiber expects a function"); | ||
| 431 | + } else if (!args.IsConstructCall()) { | ||
| 432 | + Handle<Value> argv[1] = { args[0] }; | ||
| 433 | + return uni::Return(uni::Deref(Isolate::GetCurrent(), tmpl)->GetFunction()->NewInstance(1, argv), args); | ||
| 434 | + } | ||
| 435 | + | ||
| 436 | + Handle<Function> fn = Handle<Function>::Cast(args[0]); | ||
| 437 | + new Fiber(args.This(), fn, uni::GetCurrentContext(Isolate::GetCurrent())); | ||
| 438 | + return uni::Return(args.This(), args); | ||
| 439 | + } | ||
| 440 | + | ||
| 441 | + /** | ||
| 442 | + * Begin or resume the current fiber. If the fiber is not currently running a new context will | ||
| 443 | + * be created and the callback will start. Otherwise we switch back into the exist context. | ||
| 444 | + */ | ||
| 445 | + static uni::FunctionType Run(const uni::Arguments& args) { | ||
| 446 | + Fiber& that = Unwrap(args.Holder()); | ||
| 447 | + | ||
| 448 | + // There seems to be no better place to put this check.. | ||
| 449 | + DestroyOrphans(); | ||
| 450 | + | ||
| 451 | + if (that.started && !that.yielding) { | ||
| 452 | + THROW(Exception::Error, "This Fiber is already running"); | ||
| 453 | + } else if (args.Length() > 1) { | ||
| 454 | + THROW(Exception::TypeError, "run() excepts 1 or no arguments"); | ||
| 455 | + } | ||
| 456 | + | ||
| 457 | + if (!that.started) { | ||
| 458 | + // Create a new context with entry point `Fiber::RunFiber()`. | ||
| 459 | + void** data = new void*[2]; | ||
| 460 | + data[0] = (void*)&args; | ||
| 461 | + data[1] = &that; | ||
| 462 | + that.this_fiber = Coroutine::create_fiber((void (*)(void*))RunFiber, data); | ||
| 463 | + if (!that.this_fiber) { | ||
| 464 | + delete[] data; | ||
| 465 | + THROW(Exception::RangeError, "Out of memory"); | ||
| 466 | + } | ||
| 467 | + that.started = true; | ||
| 468 | + uni::AdjustAmountOfExternalAllocatedMemory(that.isolate, that.this_fiber->size() * GC_ADJUST); | ||
| 469 | + } else { | ||
| 470 | + // If the fiber is currently running put the first parameter to `run()` on `yielded`, then | ||
| 471 | + // the pending call to `yield()` will return that value. `yielded` in this case is just a | ||
| 472 | + // misnomer, we're just reusing the same handle. | ||
| 473 | + that.yielded_exception = false; | ||
| 474 | + if (args.Length()) { | ||
| 475 | + uni::Reset(that.isolate, that.yielded, args[0]); | ||
| 476 | + } else { | ||
| 477 | + uni::Reset<Value>(that.isolate, that.yielded, uni::Undefined(that.isolate)); | ||
| 478 | + } | ||
| 479 | + } | ||
| 480 | + that.SwapContext(); | ||
| 481 | + return uni::Return(that.ReturnYielded(), args); | ||
| 482 | + } | ||
| 483 | + | ||
| 484 | + /** | ||
| 485 | + * Throw an exception into a currently yielding fiber. | ||
| 486 | + */ | ||
| 487 | + static uni::FunctionType ThrowInto(const uni::Arguments& args) { | ||
| 488 | + Fiber& that = Unwrap(args.Holder()); | ||
| 489 | + | ||
| 490 | + if (!that.yielding) { | ||
| 491 | + THROW(Exception::Error, "This Fiber is not yielding"); | ||
| 492 | + } else if (args.Length() == 0) { | ||
| 493 | + uni::Reset<Value>(that.isolate, that.yielded, uni::Undefined(that.isolate)); | ||
| 494 | + } else if (args.Length() == 1) { | ||
| 495 | + uni::Reset(that.isolate, that.yielded, args[0]); | ||
| 496 | + } else { | ||
| 497 | + THROW(Exception::TypeError, "throwInto() expects 1 or no arguments"); | ||
| 498 | + } | ||
| 499 | + that.yielded_exception = true; | ||
| 500 | + that.SwapContext(); | ||
| 501 | + return uni::Return(that.ReturnYielded(), args); | ||
| 502 | + } | ||
| 503 | + | ||
| 504 | + /** | ||
| 505 | + * Unwinds a currently running fiber. If the fiber is not running then this function has no | ||
| 506 | + * effect. | ||
| 507 | + */ | ||
| 508 | + static uni::FunctionType Reset(const uni::Arguments& args) { | ||
| 509 | + Fiber& that = Unwrap(args.Holder()); | ||
| 510 | + | ||
| 511 | + if (!that.started) { | ||
| 512 | + return uni::Return(uni::Undefined(that.isolate), args); | ||
| 513 | + } else if (!that.yielding) { | ||
| 514 | + THROW(Exception::Error, "This Fiber is not yielding"); | ||
| 515 | + } else if (args.Length()) { | ||
| 516 | + THROW(Exception::TypeError, "reset() expects no arguments"); | ||
| 517 | + } | ||
| 518 | + | ||
| 519 | + that.resetting = true; | ||
| 520 | + that.UnwindStack(); | ||
| 521 | + that.resetting = false; | ||
| 522 | + that.MakeWeak(); | ||
| 523 | + | ||
| 524 | + Handle<Value> val = uni::Deref(that.isolate, that.yielded); | ||
| 525 | + uni::Dispose(that.isolate, that.yielded); | ||
| 526 | + if (that.yielded_exception) { | ||
| 527 | + return uni::Return(uni::ThrowException(that.isolate, val), args); | ||
| 528 | + } else { | ||
| 529 | + return uni::Return(val, args); | ||
| 530 | + } | ||
| 531 | + } | ||
| 532 | + | ||
| 533 | + /** | ||
| 534 | + * Turns the fiber into a zombie and unwinds its whole stack. | ||
| 535 | + * | ||
| 536 | + * After calling this function you must either destroy this fiber or call MakeWeak() or it will | ||
| 537 | + * be leaked. | ||
| 538 | + */ | ||
| 539 | + void UnwindStack() { | ||
| 540 | + assert(!zombie); | ||
| 541 | + assert(started); | ||
| 542 | + assert(yielding); | ||
| 543 | + zombie = true; | ||
| 544 | + | ||
| 545 | + // Setup an exception which will be thrown and rethrown from Fiber::Yield() | ||
| 546 | + Handle<Value> zombie_exception = Exception::Error(uni::NewLatin1String(isolate, "This Fiber is a zombie")); | ||
| 547 | + uni::Reset(isolate, this->zombie_exception, zombie_exception); | ||
| 548 | + uni::Reset(isolate, yielded, zombie_exception); | ||
| 549 | + yielded_exception = true; | ||
| 550 | + | ||
| 551 | + // Swap context back to Fiber::Yield() which will throw an exception to unwind the stack. | ||
| 552 | + // Futher calls to yield from this fiber will rethrow the same exception. | ||
| 553 | + SwapContext(); | ||
| 554 | + assert(!started); | ||
| 555 | + zombie = false; | ||
| 556 | + | ||
| 557 | + // Make sure this is the exception we threw | ||
| 558 | + if (yielded_exception && yielded == zombie_exception) { | ||
| 559 | + yielded_exception = false; | ||
| 560 | + uni::Dispose(isolate, yielded); | ||
| 561 | + uni::Reset<Value>(isolate, yielded, uni::Undefined(isolate)); | ||
| 562 | + } | ||
| 563 | + uni::Dispose(isolate, this->zombie_exception); | ||
| 564 | + } | ||
| 565 | + | ||
| 566 | + /** | ||
| 567 | + * Common logic between Run(), ThrowInto(), and UnwindStack(). This is essentially just a | ||
| 568 | + * wrapper around this->fiber->() which also handles all the bookkeeping needed. | ||
| 569 | + */ | ||
| 570 | + void SwapContext() { | ||
| 571 | + | ||
| 572 | + entry_fiber = &Coroutine::current(); | ||
| 573 | + Fiber* last_fiber = current; | ||
| 574 | + current = this; | ||
| 575 | + | ||
| 576 | + // This will jump into either `RunFiber()` or `Yield()`, depending on if the fiber was | ||
| 577 | + // already running. | ||
| 578 | + { | ||
| 579 | + Unlocker unlocker(isolate); | ||
| 580 | + uni::ReverseIsolateScope isolate_scope(isolate); | ||
| 581 | + this_fiber->run(); | ||
| 582 | + } | ||
| 583 | + | ||
| 584 | + // At this point the fiber either returned or called `yield()`. | ||
| 585 | + current = last_fiber; | ||
| 586 | + } | ||
| 587 | + | ||
| 588 | + /** | ||
| 589 | + * Grabs and resets this fiber's yielded value. | ||
| 590 | + */ | ||
| 591 | + Handle<Value> ReturnYielded() { | ||
| 592 | + Handle<Value> val = uni::Deref(isolate, yielded); | ||
| 593 | + uni::Dispose(isolate, yielded); | ||
| 594 | + if (yielded_exception) { | ||
| 595 | + return uni::ThrowException(isolate, val); | ||
| 596 | + } else { | ||
| 597 | + return val; | ||
| 598 | + } | ||
| 599 | + } | ||
| 600 | + | ||
| 601 | + /** | ||
| 602 | + * This is the entry point for a new fiber, from `run()`. | ||
| 603 | + */ | ||
| 604 | + static void RunFiber(void** data) { | ||
| 605 | + const uni::Arguments* args = (const uni::Arguments*)data[0]; | ||
| 606 | + Fiber& that = *(Fiber*)data[1]; | ||
| 607 | + delete[] data; | ||
| 608 | + | ||
| 609 | + // New C scope so that the stack-allocated objects will be destroyed before calling | ||
| 610 | + // Coroutine::finish, because that function may not return, in which case the destructors in | ||
| 611 | + // this function won't be called. | ||
| 612 | + { | ||
| 613 | + Locker locker(that.isolate); | ||
| 614 | + Isolate::Scope isolate_scope(that.isolate); | ||
| 615 | + uni::HandleScope scope(that.isolate); | ||
| 616 | + | ||
| 617 | + // Set the stack guard for this "thread"; allow 6k of padding past the JS limit for | ||
| 618 | + // native v8 code to run | ||
| 619 | + uni::SetStackGuard(that.isolate, reinterpret_cast<char*>(that.this_fiber->bottom()) + 1024 * 6); | ||
| 620 | + | ||
| 621 | + TryCatch try_catch; | ||
| 622 | + that.ClearWeak(); | ||
| 623 | + Handle<Context> v8_context = uni::Deref(that.isolate, that.v8_context); | ||
| 624 | + v8_context->Enter(); | ||
| 625 | + | ||
| 626 | + // Workaround for v8 issue #1180 | ||
| 627 | + // http://code.google.com/p/v8/issues/detail?id=1180 | ||
| 628 | + Script::Compile(uni::NewLatin1String(that.isolate, "void 0;")); | ||
| 629 | + | ||
| 630 | + Handle<Value> yielded; | ||
| 631 | + if (args->Length()) { | ||
| 632 | + Handle<Value> argv[1] = { (*args)[0] }; | ||
| 633 | + yielded = uni::Deref(that.isolate, that.cb)->Call(v8_context->Global(), 1, argv); | ||
| 634 | + } else { | ||
| 635 | + yielded = uni::Deref(that.isolate, that.cb)->Call(v8_context->Global(), 0, NULL); | ||
| 636 | + } | ||
| 637 | + | ||
| 638 | + if (try_catch.HasCaught()) { | ||
| 639 | + uni::Reset(that.isolate, that.yielded, try_catch.Exception()); | ||
| 640 | + that.yielded_exception = true; | ||
| 641 | + if (that.zombie && !that.resetting && !uni::Deref(that.isolate, that.yielded)->StrictEquals(uni::Deref(that.isolate, that.zombie_exception))) { | ||
| 642 | + // Throwing an exception from a garbage sweep | ||
| 643 | + uni::Reset(that.isolate, fatal_stack, try_catch.StackTrace()); | ||
| 644 | + } | ||
| 645 | + } else { | ||
| 646 | + uni::Reset(that.isolate, that.yielded, yielded); | ||
| 647 | + that.yielded_exception = false; | ||
| 648 | + } | ||
| 649 | + | ||
| 650 | + // Do not invoke the garbage collector if there's no context on the stack. It will seg fault | ||
| 651 | + // otherwise. | ||
| 652 | + uni::AdjustAmountOfExternalAllocatedMemory(that.isolate, -(int)(that.this_fiber->size() * GC_ADJUST)); | ||
| 653 | + | ||
| 654 | + // Don't make weak until after notifying the garbage collector. Otherwise it may try and | ||
| 655 | + // free this very fiber! | ||
| 656 | + if (!that.zombie) { | ||
| 657 | + that.MakeWeak(); | ||
| 658 | + } | ||
| 659 | + | ||
| 660 | + // Now safe to leave the context, this stack is done with JS. | ||
| 661 | + v8_context->Exit(); | ||
| 662 | + } | ||
| 663 | + | ||
| 664 | + // The function returned (instead of yielding). | ||
| 665 | + that.started = false; | ||
| 666 | + that.this_fiber->finish(*that.entry_fiber, that.isolate); | ||
| 667 | + } | ||
| 668 | + | ||
| 669 | + /** | ||
| 670 | + * Yield control back to the function that called `run()`. The first parameter to this function | ||
| 671 | + * is returned from `run()`. The context is saved, to be later resumed from `run()`. | ||
| 672 | + * note: sigh, there is a #define Yield() in WinBase.h on Windows | ||
| 673 | + */ | ||
| 674 | + static uni::FunctionType Yield_(const uni::Arguments& args) { | ||
| 675 | + if (current == NULL) { | ||
| 676 | + THROW(Exception::Error, "yield() called with no fiber running"); | ||
| 677 | + } | ||
| 678 | + | ||
| 679 | + Fiber& that = *current; | ||
| 680 | + | ||
| 681 | + if (that.zombie) { | ||
| 682 | + return uni::Return(uni::ThrowException(that.isolate, uni::Deref(that.isolate, that.zombie_exception)), args); | ||
| 683 | + } else if (args.Length() == 0) { | ||
| 684 | + uni::Reset<Value>(that.isolate, that.yielded, Undefined(that.isolate)); | ||
| 685 | + } else if (args.Length() == 1) { | ||
| 686 | + uni::Reset(that.isolate, that.yielded, args[0]); | ||
| 687 | + } else { | ||
| 688 | + THROW(Exception::TypeError, "yield() expects 1 or no arguments"); | ||
| 689 | + } | ||
| 690 | + that.yielded_exception = false; | ||
| 691 | + | ||
| 692 | + // While not running this can be garbage collected if no one has a handle. | ||
| 693 | + that.MakeWeak(); | ||
| 694 | + | ||
| 695 | + // Return control back to `Fiber::run()`. While control is outside this function we mark it as | ||
| 696 | + // ok to garbage collect. If no one ever has a handle to resume the function it's harmful to | ||
| 697 | + // keep the handle around. | ||
| 698 | + { | ||
| 699 | + Unlocker unlocker(that.isolate); | ||
| 700 | + uni::ReverseIsolateScope isolate_scope(that.isolate); | ||
| 701 | + that.yielding = true; | ||
| 702 | + that.entry_fiber->run(); | ||
| 703 | + that.yielding = false; | ||
| 704 | + } | ||
| 705 | + // Now `run()` has been called again. | ||
| 706 | + | ||
| 707 | + // Don't garbage collect anymore! | ||
| 708 | + that.ClearWeak(); | ||
| 709 | + | ||
| 710 | + // Return the yielded value | ||
| 711 | + return uni::Return(that.ReturnYielded(), args); | ||
| 712 | + } | ||
| 713 | + | ||
| 714 | + /** | ||
| 715 | + * Getters for `started`, and `current`. | ||
| 716 | + */ | ||
| 717 | + static uni::FunctionType GetStarted(Local<String> property, const uni::GetterCallbackInfo& info) { | ||
| 718 | + if (info.This().IsEmpty() || info.This()->InternalFieldCount() != 1) { | ||
| 719 | + return uni::Return(uni::Undefined(Isolate::GetCurrent()), info); | ||
| 720 | + } | ||
| 721 | + Fiber& that = Unwrap(info.This()); | ||
| 722 | + return uni::Return(uni::NewBoolean(that.isolate, that.started), info); | ||
| 723 | + } | ||
| 724 | + | ||
| 725 | + static uni::FunctionType GetCurrent(Local<String> property, const uni::GetterCallbackInfo& info) { | ||
| 726 | + if (current) { | ||
| 727 | + return uni::Return(current->handle, info); | ||
| 728 | + } else { | ||
| 729 | + return uni::Return(uni::Undefined(Isolate::GetCurrent()), info); | ||
| 730 | + } | ||
| 731 | + } | ||
| 732 | + | ||
| 733 | + /** | ||
| 734 | + * Allow access to coroutine pool size | ||
| 735 | + */ | ||
| 736 | + static uni::FunctionType GetPoolSize(Local<String> property, const uni::GetterCallbackInfo& info) { | ||
| 737 | + return uni::Return(uni::NewNumber(Isolate::GetCurrent(), Coroutine::pool_size), info); | ||
| 738 | + } | ||
| 739 | + | ||
| 740 | + static void SetPoolSize(Local<String> property, Local<Value> value, const uni::SetterCallbackInfo& info) { | ||
| 741 | + Coroutine::pool_size = value->ToNumber()->Value(); | ||
| 742 | + } | ||
| 743 | + | ||
| 744 | + /** | ||
| 745 | + * Return number of fibers that have been created | ||
| 746 | + */ | ||
| 747 | + static uni::FunctionType GetFibersCreated(Local<String> property, const uni::GetterCallbackInfo& info) { | ||
| 748 | + return uni::Return(uni::NewNumber(Isolate::GetCurrent(), Coroutine::coroutines_created()), info); | ||
| 749 | + } | ||
| 750 | + | ||
| 751 | + public: | ||
| 752 | + /** | ||
| 753 | + * Initialize the Fiber library. | ||
| 754 | + */ | ||
| 755 | + static void Init(Handle<Object> target) { | ||
| 756 | + // Use a locker which won't get destroyed when this library gets unloaded. This is a hack | ||
| 757 | + // to prevent v8 from trying to clean up this "thread" while the whole application is | ||
| 758 | + // shutting down. TODO: There's likely a better way to accomplish this, but since the | ||
| 759 | + // application is going down lost memory isn't the end of the world. But with a regular lock | ||
| 760 | + // there's seg faults when node shuts down. | ||
| 761 | + Isolate* isolate = Isolate::GetCurrent(); | ||
| 762 | + global_locker = new Locker(isolate); | ||
| 763 | + current = NULL; | ||
| 764 | + | ||
| 765 | + // Fiber constructor | ||
| 766 | + Handle<FunctionTemplate> tmpl = uni::NewFunctionTemplate(isolate, New); | ||
| 767 | + uni::Reset(isolate, Fiber::tmpl, tmpl); | ||
| 768 | + tmpl->SetClassName(uni::NewLatin1Symbol(isolate, "Fiber")); | ||
| 769 | + | ||
| 770 | + // Guard which only allows these methods to be called on a fiber; prevents | ||
| 771 | + // `fiber.run.call({})` from seg faulting. | ||
| 772 | + Handle<Signature> sig = uni::NewSignature(isolate, tmpl); | ||
| 773 | + tmpl->InstanceTemplate()->SetInternalFieldCount(1); | ||
| 774 | + | ||
| 775 | + // Fiber.prototype | ||
| 776 | + Handle<ObjectTemplate> proto = tmpl->PrototypeTemplate(); | ||
| 777 | + proto->Set(uni::NewLatin1Symbol(isolate, "reset"), | ||
| 778 | + uni::NewFunctionTemplate(isolate, Reset, Handle<Value>(), sig)); | ||
| 779 | + proto->Set(uni::NewLatin1Symbol(isolate, "run"), | ||
| 780 | + uni::NewFunctionTemplate(isolate, Run, Handle<Value>(), sig)); | ||
| 781 | + proto->Set(uni::NewLatin1Symbol(isolate, "throwInto"), | ||
| 782 | + uni::NewFunctionTemplate(isolate, ThrowInto, Handle<Value>(), sig)); | ||
| 783 | + proto->SetAccessor(uni::NewLatin1Symbol(isolate, "started"), GetStarted); | ||
| 784 | + | ||
| 785 | + // Global yield() function | ||
| 786 | + Handle<Function> yield = uni::NewFunctionTemplate(isolate, Yield_)->GetFunction(); | ||
| 787 | + Handle<String> sym_yield = uni::NewLatin1Symbol(isolate, "yield"); | ||
| 788 | + target->Set(sym_yield, yield); | ||
| 789 | + | ||
| 790 | + // Fiber properties | ||
| 791 | + Handle<Function> fn = tmpl->GetFunction(); | ||
| 792 | + fn->Set(sym_yield, yield); | ||
| 793 | + fn->SetAccessor(uni::NewLatin1Symbol(isolate, "current"), GetCurrent); | ||
| 794 | + fn->SetAccessor(uni::NewLatin1Symbol(isolate, "poolSize"), GetPoolSize, SetPoolSize); | ||
| 795 | + fn->SetAccessor(uni::NewLatin1Symbol(isolate, "fibersCreated"), GetFibersCreated); | ||
| 796 | + | ||
| 797 | + // Global Fiber | ||
| 798 | + target->Set(uni::NewLatin1Symbol(isolate, "Fiber"), fn); | ||
| 799 | + uni::Reset(isolate, fiber_object, fn); | ||
| 800 | + } | ||
| 801 | +}; | ||
| 802 | + | ||
| 803 | +Persistent<FunctionTemplate> Fiber::tmpl; | ||
| 804 | +Persistent<Function> Fiber::fiber_object; | ||
| 805 | +Locker* Fiber::global_locker; | ||
| 806 | +Fiber* Fiber::current = NULL; | ||
| 807 | +vector<Fiber*> Fiber::orphaned_fibers; | ||
| 808 | +Persistent<Value> Fiber::fatal_stack; | ||
| 809 | +bool did_init = false; | ||
| 810 | + | ||
| 811 | +#if !NODE_VERSION_AT_LEAST(0,10,0) | ||
| 812 | +extern "C" | ||
| 813 | +#endif | ||
| 814 | +void init(Handle<Object> target) { | ||
| 815 | + Isolate* isolate = Isolate::GetCurrent(); | ||
| 816 | + if (did_init || !target->Get(uni::NewLatin1Symbol(isolate, "Fiber"))->IsUndefined()) { | ||
| 817 | + // Oh god. Node will call init() twice even though the library was loaded only once. See Node | ||
| 818 | + // issue #2621 (no fix). | ||
| 819 | + return; | ||
| 820 | + } | ||
| 821 | + did_init = true; | ||
| 822 | + uni::HandleScope scope(isolate); | ||
| 823 | + Coroutine::init(isolate); | ||
| 824 | + Fiber::Init(target); | ||
| 825 | + // Default stack size of either 512k or 1M. Perhaps make this configurable by the run time? | ||
| 826 | + Coroutine::set_stack_size(128 * 1024); | ||
| 827 | +} | ||
| 828 | + | ||
| 829 | +NODE_MODULE(fibers, init) |
| 1 | +Copyright (c) 2000-2009 Marc Alexander Lehmann <schmorp@schmorp.de> | ||
| 2 | + | ||
| 3 | +Redistribution and use in source and binary forms, with or without modifica- | ||
| 4 | +tion, are permitted provided that the following conditions are met: | ||
| 5 | + | ||
| 6 | + 1. Redistributions of source code must retain the above copyright notice, | ||
| 7 | + this list of conditions and the following disclaimer. | ||
| 8 | + | ||
| 9 | + 2. Redistributions in binary form must reproduce the above copyright | ||
| 10 | + notice, this list of conditions and the following disclaimer in the | ||
| 11 | + documentation and/or other materials provided with the distribution. | ||
| 12 | + | ||
| 13 | +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 14 | +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- | ||
| 15 | +CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| 16 | +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- | ||
| 17 | +CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 18 | +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 19 | +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 20 | +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- | ||
| 21 | +ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
| 22 | +OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 23 | + | ||
| 24 | +Alternatively, the following files carry an additional notice that | ||
| 25 | +explicitly allows relicensing under the GPLv2: coro.c, coro.h. | ||
| 26 | + |
| 1 | +/* | ||
| 2 | + * This file was taken from pth-1.40/aclocal.m4 | ||
| 3 | + * The original copyright is below. | ||
| 4 | + * | ||
| 5 | + * GNU Pth - The GNU Portable Threads | ||
| 6 | + * Copyright (c) 1999-2001 Ralf S. Engelschall <rse@engelschall.com> | ||
| 7 | + * | ||
| 8 | + * This file is part of GNU Pth, a non-preemptive thread scheduling | ||
| 9 | + * library which can be found at http://www.gnu.org/software/pth/. | ||
| 10 | + * | ||
| 11 | + * This file is free software; you can redistribute it and/or | ||
| 12 | + * modify it under the terms of the GNU Lesser General Public | ||
| 13 | + * License as published by the Free Software Foundation; either | ||
| 14 | + * version 2.1 of the License, or (at your option) any later version. | ||
| 15 | + * | ||
| 16 | + * This file is distributed in the hope that it will be useful, | ||
| 17 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 19 | + * Lesser General Public License for more details. | ||
| 20 | + * | ||
| 21 | + * You should have received a copy of the GNU Lesser General Public | ||
| 22 | + * License along with this file; if not, write to the Free Software | ||
| 23 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||
| 24 | + * USA, or contact Marc Lehmann <schmorp@schmorp.de>. | ||
| 25 | + */ | ||
| 26 | + | ||
| 27 | +#include <stdio.h> | ||
| 28 | +#include <stdlib.h> | ||
| 29 | +#include <string.h> | ||
| 30 | +#if defined(TEST_sigstack) || defined(TEST_sigaltstack) | ||
| 31 | +#include <sys/types.h> | ||
| 32 | +#include <signal.h> | ||
| 33 | +#include <unistd.h> | ||
| 34 | +#endif | ||
| 35 | +#if defined(TEST_makecontext) | ||
| 36 | +#include <ucontext.h> | ||
| 37 | +#endif | ||
| 38 | +union alltypes { | ||
| 39 | + long l; | ||
| 40 | + double d; | ||
| 41 | + void *vp; | ||
| 42 | + void (*fp)(void); | ||
| 43 | + char *cp; | ||
| 44 | +}; | ||
| 45 | +static volatile char *handler_addr = (char *)0xDEAD; | ||
| 46 | +#if defined(TEST_sigstack) || defined(TEST_sigaltstack) | ||
| 47 | +static volatile int handler_done = 0; | ||
| 48 | +void handler(int sig) | ||
| 49 | +{ | ||
| 50 | + char garbage[1024]; | ||
| 51 | + int i; | ||
| 52 | + auto int dummy; | ||
| 53 | + for (i = 0; i < 1024; i++) | ||
| 54 | + garbage[i] = 'X'; | ||
| 55 | + handler_addr = (char *)&dummy; | ||
| 56 | + handler_done = 1; | ||
| 57 | + return; | ||
| 58 | +} | ||
| 59 | +#endif | ||
| 60 | +#if defined(TEST_makecontext) | ||
| 61 | +static ucontext_t uc_handler; | ||
| 62 | +static ucontext_t uc_main; | ||
| 63 | +void handler(void) | ||
| 64 | +{ | ||
| 65 | + char garbage[1024]; | ||
| 66 | + int i; | ||
| 67 | + auto int dummy; | ||
| 68 | + for (i = 0; i < 1024; i++) | ||
| 69 | + garbage[i] = 'X'; | ||
| 70 | + handler_addr = (char *)&dummy; | ||
| 71 | + swapcontext(&uc_handler, &uc_main); | ||
| 72 | + return; | ||
| 73 | +} | ||
| 74 | +#endif | ||
| 75 | +int main(int argc, char *argv[]) | ||
| 76 | +{ | ||
| 77 | + FILE *f; | ||
| 78 | + char *skaddr; | ||
| 79 | + char *skbuf; | ||
| 80 | + int sksize; | ||
| 81 | + char result[1024]; | ||
| 82 | + int i; | ||
| 83 | + sksize = 32768; | ||
| 84 | + skbuf = (char *)malloc(sksize*2+2*sizeof(union alltypes)); | ||
| 85 | + if (skbuf == NULL) | ||
| 86 | + exit(1); | ||
| 87 | + for (i = 0; i < sksize*2+2*sizeof(union alltypes); i++) | ||
| 88 | + skbuf[i] = 'A'; | ||
| 89 | + skaddr = skbuf+sizeof(union alltypes); | ||
| 90 | +#if defined(TEST_sigstack) || defined(TEST_sigaltstack) | ||
| 91 | + { | ||
| 92 | + struct sigaction sa; | ||
| 93 | +#if defined(TEST_sigstack) | ||
| 94 | + struct sigstack ss; | ||
| 95 | +#elif defined(TEST_sigaltstack) && defined(HAVE_STACK_T) | ||
| 96 | + stack_t ss; | ||
| 97 | +#else | ||
| 98 | + struct sigaltstack ss; | ||
| 99 | +#endif | ||
| 100 | +#if defined(TEST_sigstack) | ||
| 101 | + ss.ss_sp = (void *)(skaddr + sksize); | ||
| 102 | + ss.ss_onstack = 0; | ||
| 103 | + if (sigstack(&ss, NULL) < 0) | ||
| 104 | + exit(1); | ||
| 105 | +#elif defined(TEST_sigaltstack) | ||
| 106 | + ss.ss_sp = (void *)(skaddr + sksize); | ||
| 107 | + ss.ss_size = sksize; | ||
| 108 | + ss.ss_flags = 0; | ||
| 109 | + if (sigaltstack(&ss, NULL) < 0) | ||
| 110 | + exit(1); | ||
| 111 | +#endif | ||
| 112 | + memset((void *)&sa, 0, sizeof(struct sigaction)); | ||
| 113 | + sa.sa_handler = handler; | ||
| 114 | + sa.sa_flags = SA_ONSTACK; | ||
| 115 | + sigemptyset(&sa.sa_mask); | ||
| 116 | + sigaction(SIGUSR1, &sa, NULL); | ||
| 117 | + kill(getpid(), SIGUSR1); | ||
| 118 | + while (!handler_done) | ||
| 119 | + /*nop*/; | ||
| 120 | + } | ||
| 121 | +#endif | ||
| 122 | +#if defined(TEST_makecontext) | ||
| 123 | + { | ||
| 124 | + if (getcontext(&uc_handler) != 0) | ||
| 125 | + exit(1); | ||
| 126 | + uc_handler.uc_link = NULL; | ||
| 127 | + uc_handler.uc_stack.ss_sp = (void *)(skaddr + sksize); | ||
| 128 | + uc_handler.uc_stack.ss_size = sksize; | ||
| 129 | + uc_handler.uc_stack.ss_flags = 0; | ||
| 130 | + makecontext(&uc_handler, handler, 1); | ||
| 131 | + swapcontext(&uc_main, &uc_handler); | ||
| 132 | + } | ||
| 133 | +#endif | ||
| 134 | + if (handler_addr == (char *)0xDEAD) | ||
| 135 | + exit(1); | ||
| 136 | + if (handler_addr < skaddr+sksize) { | ||
| 137 | + /* stack was placed into lower area */ | ||
| 138 | + if (*(skaddr+sksize) != 'A') | ||
| 139 | + sprintf(result, "(skaddr)+(sksize)-%d,(sksize)-%d", | ||
| 140 | + sizeof(union alltypes), sizeof(union alltypes)); | ||
| 141 | + else | ||
| 142 | + strcpy(result, "(skaddr)+(sksize),(sksize)"); | ||
| 143 | + } | ||
| 144 | + else { | ||
| 145 | + /* stack was placed into higher area */ | ||
| 146 | + if (*(skaddr+sksize*2) != 'A') | ||
| 147 | + sprintf(result, "(skaddr),(sksize)-%d", sizeof(union alltypes)); | ||
| 148 | + else | ||
| 149 | + strcpy(result, "(skaddr),(sksize)"); | ||
| 150 | + } | ||
| 151 | + printf("%s\n", result); | ||
| 152 | + exit(0); | ||
| 153 | +} | ||
| 154 | + |
-
请 注册 或 登录 后发表评论