зеркало из https://github.com/mozilla/gecko-dev.git
servo: move JS stuff out of servo into mozjs; redefine geom types to
make use of inherited mutability Source-Repo: https://github.com/servo/servo Source-Revision: 902f81ca3fd37d0e739c79a55cfe21746f9e3178
This commit is contained in:
Родитель
11bc38de9a
Коммит
4b39cef9ee
|
@ -5,7 +5,7 @@ import result::extensions;
|
|||
import dom::rcu::writer_methods;
|
||||
import dom=dom::base;
|
||||
import layout::layout;
|
||||
import js::methods;
|
||||
import js::rust::methods;
|
||||
|
||||
enum msg {
|
||||
parse(str),
|
||||
|
@ -32,7 +32,7 @@ fn join_layout(scope: dom::node_scope,
|
|||
fn content(to_layout: chan<layout::msg>) -> chan<msg> {
|
||||
task::spawn_listener::<msg> {|from_master|
|
||||
let scope = dom::node_scope();
|
||||
let rt = js::rt();
|
||||
let rt = js::rust::rt();
|
||||
loop {
|
||||
alt from_master.recv() {
|
||||
parse(filename) {
|
||||
|
@ -65,8 +65,8 @@ fn content(to_layout: chan<layout::msg>) -> chan<msg> {
|
|||
let cx = rt.cx();
|
||||
cx.set_default_options_and_version();
|
||||
cx.set_logging_error_reporter();
|
||||
cx.new_compartment(jsglobal::global_class).chain { |comp|
|
||||
comp.define_functions(jsglobal::global_fns);
|
||||
cx.new_compartment(js::global::global_class).chain { |comp|
|
||||
comp.define_functions(js::global::debug_fns);
|
||||
cx.evaluate_script(comp.global_obj, bytes, filename, 1u)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,185 +0,0 @@
|
|||
import spidermonkey::*;
|
||||
import spidermonkey::jsapi::*;
|
||||
import spidermonkey::jsapi::bindgen::*;
|
||||
import ptr::{null, addr_of};
|
||||
import result::{result, ok, err, extensions};
|
||||
import libc::c_char;
|
||||
import name_pool::{name_pool, methods};
|
||||
import str::unsafe::from_c_str;
|
||||
import io::writer_util;
|
||||
|
||||
export rt;
|
||||
export methods;
|
||||
export cx;
|
||||
export jsobj;
|
||||
|
||||
const default_heapsize: u32 = 8_u32 * 1024_u32 * 1024_u32;
|
||||
const default_stacksize: uint = 8192u;
|
||||
const ERR: JSBool = 0_i32;
|
||||
|
||||
fn result(n: JSBool) -> result<(),()> {
|
||||
if n != ERR {ok(())} else {err(())}
|
||||
}
|
||||
|
||||
type named_functions = @{
|
||||
names: [str],
|
||||
funcs: [JSFunctionSpec]
|
||||
};
|
||||
|
||||
// ___________________________________________________________________________
|
||||
// runtimes
|
||||
|
||||
type rt = @rt_rsrc;
|
||||
|
||||
resource rt_rsrc(self: {ptr: *JSRuntime}) {
|
||||
JS_Finish(self.ptr)
|
||||
}
|
||||
|
||||
fn rt() -> rt {
|
||||
@rt_rsrc({ptr: JS_Init(default_heapsize)})
|
||||
}
|
||||
|
||||
impl methods for rt {
|
||||
fn cx() -> cx {
|
||||
@cx_rsrc({ptr: JS_NewContext(self.ptr, default_stacksize),
|
||||
rt: self})
|
||||
}
|
||||
}
|
||||
|
||||
// ___________________________________________________________________________
|
||||
// contexts
|
||||
|
||||
type cx = @cx_rsrc;
|
||||
resource cx_rsrc(self: {ptr: *JSContext, rt: rt}) {
|
||||
JS_DestroyContext(self.ptr);
|
||||
}
|
||||
|
||||
impl methods for cx {
|
||||
fn rooted_obj(obj: *JSObject) -> jsobj {
|
||||
let jsobj = @jsobj_rsrc({cx: self, cxptr: self.ptr, ptr: obj});
|
||||
JS_AddObjectRoot(self.ptr, ptr::addr_of(jsobj.ptr));
|
||||
jsobj
|
||||
}
|
||||
|
||||
fn set_default_options_and_version() {
|
||||
self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
|
||||
self.set_version(JSVERSION_LATEST);
|
||||
}
|
||||
|
||||
fn set_options(v: jsuint) {
|
||||
JS_SetOptions(self.ptr, v);
|
||||
}
|
||||
|
||||
fn set_version(v: i32) {
|
||||
JS_SetVersion(self.ptr, v);
|
||||
}
|
||||
|
||||
fn set_logging_error_reporter() {
|
||||
JS_SetErrorReporter(self.ptr, reportError);
|
||||
}
|
||||
|
||||
fn set_error_reporter(reportfn: *u8) {
|
||||
JS_SetErrorReporter(self.ptr, reportfn);
|
||||
}
|
||||
|
||||
fn new_compartment(globclsfn: fn(name_pool) -> JSClass) -> result<compartment,()> {
|
||||
let np = name_pool();
|
||||
let globcls = @globclsfn(np);
|
||||
let globobj =
|
||||
JS_NewCompartmentAndGlobalObject(
|
||||
self.ptr,
|
||||
&*globcls as *JSClass,
|
||||
null());
|
||||
result(JS_InitStandardClasses(self.ptr, globobj)).chain { |_ok|
|
||||
ok(@{cx: self,
|
||||
name_pool: np,
|
||||
global_class: globcls,
|
||||
mut global_funcs: [],
|
||||
global_obj: self.rooted_obj(globobj)})
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_script(glob: jsobj, bytes: [u8], filename: str,
|
||||
line_num: uint) -> result<(),()> {
|
||||
vec::as_buf(bytes) { |bytes_ptr|
|
||||
str::as_c_str(filename) { |filename_cstr|
|
||||
let bytes_ptr = bytes_ptr as *c_char;
|
||||
let v: jsval = 0_u64;
|
||||
#debug["Evaluating script from %s with bytes %?", filename, bytes];
|
||||
if JS_EvaluateScript(self.ptr, glob.ptr,
|
||||
bytes_ptr, bytes.len() as uintN,
|
||||
filename_cstr, line_num as uintN,
|
||||
ptr::addr_of(v)) == ERR {
|
||||
#debug["...err!"];
|
||||
err(())
|
||||
} else {
|
||||
// we could return the script result but then we'd have
|
||||
// to root it and so forth and, really, who cares?
|
||||
#debug["...ok!"];
|
||||
ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crust fn reportError(_cx: *JSContext,
|
||||
msg: *c_char,
|
||||
report: *JSErrorReport) {
|
||||
unsafe {
|
||||
let fnptr = (*report).filename;
|
||||
let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {"none"};
|
||||
let lineno = (*report).lineno;
|
||||
let msg = from_c_str(msg);
|
||||
#error["Error at %s:%?: %s\n", fname, lineno, msg];
|
||||
}
|
||||
}
|
||||
|
||||
// ___________________________________________________________________________
|
||||
// compartment
|
||||
|
||||
type compartment = @{
|
||||
cx: cx,
|
||||
name_pool: name_pool,
|
||||
global_class: @JSClass,
|
||||
mut global_funcs: [@[JSFunctionSpec]],
|
||||
global_obj: jsobj
|
||||
};
|
||||
|
||||
impl methods for compartment {
|
||||
fn define_functions(specfn: fn(name_pool) -> [JSFunctionSpec]) -> result<(),()> {
|
||||
let specvec = @specfn(self.name_pool);
|
||||
self.global_funcs += [specvec];
|
||||
vec::as_buf(*specvec) { |specs|
|
||||
result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ___________________________________________________________________________
|
||||
// objects
|
||||
|
||||
type jsobj = @jsobj_rsrc;
|
||||
|
||||
resource jsobj_rsrc(self: {cx: cx, cxptr: *JSContext, ptr: *JSObject}) {
|
||||
JS_RemoveObjectRoot(self.cxptr, ptr::addr_of(self.ptr));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
#[test]
|
||||
fn dummy() {
|
||||
let rt = rt();
|
||||
let cx = rt.cx();
|
||||
cx.set_default_options_and_version();
|
||||
cx.set_logging_error_reporter();
|
||||
cx.new_compartment(jsglobal::global_class).chain { |comp|
|
||||
comp.define_functions(jsglobal::global_fns);
|
||||
|
||||
let bytes = str::bytes("debug(22);");
|
||||
cx.evaluate_script(comp.global_obj, bytes, "test", 1u)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
// Definition for the global object that we use:
|
||||
|
||||
import spidermonkey::*;
|
||||
import spidermonkey::jsapi::*;
|
||||
import spidermonkey::jsapi::bindgen::*;
|
||||
import ptr::null;
|
||||
import jsutil::*;
|
||||
import name_pool::{name_pool, methods};
|
||||
|
||||
// Can't use spidermonkey::crust::* versions due to Rust #2440
|
||||
|
||||
crust fn PropertyStub(++arg0: *JSContext, ++arg1: *JSObject, ++arg2: jsid, ++arg3: *jsval) -> JSBool {
|
||||
JS_PropertyStub(arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
crust fn StrictPropertyStub(++arg0: *JSContext, ++arg1: *JSObject, ++arg2: jsid, ++arg3: JSBool, ++arg4: *jsval) -> JSBool {
|
||||
JS_StrictPropertyStub(arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
crust fn EnumerateStub(++arg0: *JSContext, ++arg1: *JSObject) -> JSBool {
|
||||
JS_EnumerateStub(arg0, arg1)
|
||||
}
|
||||
|
||||
crust fn ResolveStub(++arg0: *JSContext,
|
||||
++arg1: *JSObject,
|
||||
++arg2: jsid) -> JSBool {
|
||||
JS_ResolveStub(arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
crust fn ConvertStub(++arg0: *JSContext,
|
||||
++arg1: *JSObject,
|
||||
++arg2: JSType,
|
||||
++arg3: *jsval) -> JSBool {
|
||||
JS_ConvertStub(arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
fn global_class(np: name_pool) -> JSClass {
|
||||
{name: np.add("global"),
|
||||
flags: 0x48000_u32,
|
||||
addProperty: PropertyStub,
|
||||
delProperty: PropertyStub,
|
||||
getProperty: PropertyStub,
|
||||
setProperty: StrictPropertyStub,
|
||||
enumerate: EnumerateStub,
|
||||
resolve: ResolveStub,
|
||||
convert: ConvertStub,
|
||||
finalize: null(),
|
||||
reserved0: null(),
|
||||
checkAccess: null(),
|
||||
call: null(),
|
||||
construct: null(),
|
||||
xdrObject: null(),
|
||||
hasInstance: null(),
|
||||
trace: null(),
|
||||
reserved1: null(),
|
||||
reserved: (null(), null(), null(), null(), null(), // 05
|
||||
null(), null(), null(), null(), null(), // 10
|
||||
null(), null(), null(), null(), null(), // 15
|
||||
null(), null(), null(), null(), null(), // 20
|
||||
null(), null(), null(), null(), null(), // 25
|
||||
null(), null(), null(), null(), null(), // 30
|
||||
null(), null(), null(), null(), null(), // 35
|
||||
null(), null(), null(), null(), null())} // 40
|
||||
}
|
||||
|
||||
crust fn debug(cx: *JSContext, argc: uintN, vp: *jsval) -> JSBool {
|
||||
import io::writer_util;
|
||||
|
||||
#debug["debug() called with %? arguments", argc];
|
||||
|
||||
unsafe {
|
||||
let argv = JS_ARGV(cx, vp);
|
||||
for uint::range(0u, argc as uint) { |i|
|
||||
let jsstr = JS_ValueToString(cx, argv[i]);
|
||||
let bytes = JS_EncodeString(cx, jsstr);
|
||||
let str = str::unsafe::from_c_str(bytes);
|
||||
JS_free(cx, unsafe::reinterpret_cast(bytes));
|
||||
#debug["%s", str];
|
||||
}
|
||||
JS_SET_RVAL(cx, vp, JSVAL_NULL);
|
||||
ret 1_i32;
|
||||
}
|
||||
}
|
||||
|
||||
fn global_fns(np: name_pool) -> [JSFunctionSpec] {
|
||||
[{name: np.add("debug"),
|
||||
call: debug,
|
||||
nargs: 0_u16,
|
||||
flags: 0_u16}]
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
import spidermonkey::jsapi::{JSContext, jsval};
|
||||
|
||||
impl methods<T: copy> for *T {
|
||||
unsafe fn +(idx: uint) -> *T {
|
||||
ptr::offset(self, idx)
|
||||
}
|
||||
unsafe fn [](idx: uint) -> T {
|
||||
*(self + idx)
|
||||
}
|
||||
}
|
||||
|
||||
const JSVAL_VOID: u64 = 0x0001fff2_00000000_u64;
|
||||
const JSVAL_NULL: u64 = 0x0001fff6_00000000_u64;
|
||||
const JSVAL_ZERO: u64 = 0x0001fff1_00000000_u64;
|
||||
const JSVAL_ONE: u64 = 0x0001fff1_00000001_u64;
|
||||
const JSVAL_FALSE: u64 = 0x0001fff3_00000000_u64;
|
||||
const JSVAL_TRUE: u64 = 0x0001fff3_00000001_u64;
|
||||
|
||||
unsafe fn JS_ARGV(_cx: *JSContext, vp: *jsval) -> *jsval {
|
||||
vp + 2u
|
||||
}
|
||||
|
||||
unsafe fn JS_SET_RVAL(_cx: *JSContext, vp: *jsval, v: jsval) {
|
||||
let vp: *mut jsval = unsafe::reinterpret_cast(vp);
|
||||
*vp = v;
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import libc::c_char;
|
||||
|
||||
type name_pool = @{
|
||||
mut strbufs: [str]
|
||||
};
|
||||
|
||||
fn name_pool() -> name_pool {
|
||||
@{mut strbufs: []}
|
||||
}
|
||||
|
||||
impl methods for name_pool {
|
||||
fn add(-s: str) -> *c_char {
|
||||
let c_str = str::as_c_str(s) { |bytes| bytes };
|
||||
self.strbufs += [s]; // in theory, this should *move* the str in here..
|
||||
ret c_str; // ...and so this ptr ought to be valid.
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ class attr {
|
|||
enum element_subclass {
|
||||
es_unknown,
|
||||
es_div,
|
||||
es_img(size<au>),
|
||||
es_img({mut size: size<au>}),
|
||||
es_head
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// FIXME--mut should be inherited
|
||||
type point<A> = { mut x: A, mut y: A };
|
||||
type size<A> = { mut width: A, mut height: A };
|
||||
type rect<A> = { mut origin: point<A>, mut size: size<A> };
|
||||
type point<A> = { x: A, y: A };
|
||||
type size<A> = { width: A, height: A };
|
||||
type rect<A> = { origin: point<A>, size: size<A> };
|
||||
|
||||
enum au = int;
|
||||
|
||||
|
@ -12,25 +11,25 @@ impl size for size<int> {
|
|||
}
|
||||
|
||||
fn point<A:copy>(x: A, y: A) -> point<A> {
|
||||
{mut x: x, mut y: y}
|
||||
{x: x, y: y}
|
||||
}
|
||||
|
||||
fn size<A:copy>(w: A, h: A) -> size<A> {
|
||||
{mut width: w, mut height: h}
|
||||
{width: w, height: h}
|
||||
}
|
||||
|
||||
fn box<A:copy>(x: A, y: A, w: A, h: A) -> rect<A> {
|
||||
{mut origin: point(x, y),
|
||||
mut size: size(w, h)}
|
||||
{origin: point(x, y),
|
||||
size: size(w, h)}
|
||||
}
|
||||
|
||||
fn zero_rect_au() -> rect<au> {
|
||||
let z = au(0);
|
||||
{mut origin: point(z, z), mut size: zero_size_au()}
|
||||
{origin: point(z, z), size: zero_size_au()}
|
||||
}
|
||||
|
||||
fn zero_size_au() -> size<au> {
|
||||
{mut width: au(0), mut height: au(0)}
|
||||
{width: au(0), height: au(0)}
|
||||
}
|
||||
|
||||
fn px_to_au(i: int) -> au {
|
||||
|
|
|
@ -173,12 +173,13 @@ mod test {
|
|||
fn do_layout() {
|
||||
let s = scope();
|
||||
|
||||
let n0 = s.new_node(nk_element(element("img",
|
||||
~es_img(size(au(10),au(10))))));
|
||||
let n1 = s.new_node(nk_element(element("img",
|
||||
~es_img(size(au(10),au(15))))));
|
||||
let n2 = s.new_node(nk_element(element("img",
|
||||
~es_img(size(au(10),au(20))))));
|
||||
fn mk_img(size: size<au>) -> ~dom::base::element_subclass {
|
||||
~es_img({mut size: size})
|
||||
}
|
||||
|
||||
let n0 = s.new_node(nk_element(element("img", mk_img(size(au(10),au(10))))));
|
||||
let n1 = s.new_node(nk_element(element("img", mk_img(size(au(10),au(10))))));
|
||||
let n2 = s.new_node(nk_element(element("img", mk_img(size(au(10),au(20))))));
|
||||
let n3 = s.new_node(nk_element(element("div", ~es_div)));
|
||||
|
||||
tree::add_child(s, n3, n0);
|
||||
|
|
|
@ -22,13 +22,13 @@ impl block_layout_methods for @box {
|
|||
for tree::each_child(btree, self) {|c|
|
||||
let mut blk_available_width = available_width;
|
||||
// FIXME subtract borders, margins, etc
|
||||
c.bounds.origin = {mut x: au(0), mut y: au(current_height)};
|
||||
c.bounds.origin = {x: au(0), y: au(current_height)};
|
||||
c.reflow(blk_available_width);
|
||||
current_height += *c.bounds.size.height;
|
||||
}
|
||||
|
||||
self.bounds.size = {mut width: available_width, // FIXME
|
||||
mut height: au(current_height)};
|
||||
self.bounds.size = {width: available_width, // FIXME
|
||||
height: au(current_height)};
|
||||
|
||||
#debug["reflow_block size=%?", self.bounds];
|
||||
}
|
||||
|
|
|
@ -153,9 +153,9 @@ impl box_builder_priv for node {
|
|||
~nk_text(string) { bk_text(@text_box(string)) }
|
||||
~nk_element(element) {
|
||||
alt *element.subclass {
|
||||
es_div { bk_block }
|
||||
es_img(size) { bk_intrinsic(@copy size) }
|
||||
es_unknown { bk_inline }
|
||||
es_div { bk_block }
|
||||
es_img({size}) { bk_intrinsic(@size) }
|
||||
es_unknown { bk_inline }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,15 +19,15 @@ impl inline_layout_methods for @box {
|
|||
let mut current_height = 0;
|
||||
for tree::each_child(btree, self) {
|
||||
|kid|
|
||||
kid.bounds.origin = { mut x: au(x), mut y: y };
|
||||
kid.bounds.origin = { x: au(x), y: y };
|
||||
kid.reflow(au(inline_available_width));
|
||||
inline_available_width -= *kid.bounds.size.width;
|
||||
x += *kid.bounds.size.width;
|
||||
current_height = int::max(current_height, *kid.bounds.size.height);
|
||||
}
|
||||
|
||||
self.bounds.size = { mut width: available_width,
|
||||
mut height: au(current_height) };
|
||||
self.bounds.size = { width: available_width,
|
||||
height: au(current_height) };
|
||||
|
||||
#debug["reflow_inline size=%?", self.bounds];
|
||||
}
|
||||
|
|
|
@ -27,14 +27,14 @@ impl text_layout_methods for @box {
|
|||
run.shape();
|
||||
|
||||
self.bounds.size = {
|
||||
mut width:
|
||||
width:
|
||||
alt vec::last_opt(run.glyphs.get()) {
|
||||
some(glyph) {
|
||||
au(*glyph.pos.offset.x + *glyph.pos.advance.x)
|
||||
}
|
||||
none { au(0) }
|
||||
},
|
||||
mut height: au(60 * 14)
|
||||
height: au(60 * 14)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,16 +20,16 @@ fn link_up_attribute(scope: dom::node_scope, node: dom::node, key: str,
|
|||
dom::nk_element(element) {
|
||||
element.attrs.push(~attr(key, value));
|
||||
alt *element.subclass {
|
||||
es_img(dimensions) if key == "width" {
|
||||
es_img(img) if key == "width" {
|
||||
alt int::from_str(value) {
|
||||
none { /* drop on the floor */ }
|
||||
some(s) { dimensions.width = geom::px_to_au(s); }
|
||||
some(s) { img.size.width = geom::px_to_au(s); }
|
||||
}
|
||||
}
|
||||
es_img(dimensions) if key == "height" {
|
||||
es_img(img) if key == "height" {
|
||||
alt int::from_str(value) {
|
||||
none { /* drop on the floor */ }
|
||||
some(s) { dimensions.height = geom::px_to_au(s); }
|
||||
some(s) { img.size.height = geom::px_to_au(s); }
|
||||
}
|
||||
}
|
||||
es_div | es_img(*) | es_head | es_unknown {
|
||||
|
@ -48,10 +48,10 @@ fn build_element_subclass(tag_name: str) -> ~element_subclass {
|
|||
alt tag_name {
|
||||
"div" { ret ~es_div; }
|
||||
"img" {
|
||||
ret ~es_img({
|
||||
mut width: geom::px_to_au(100),
|
||||
mut height: geom::px_to_au(100)
|
||||
});
|
||||
ret ~es_img({mut size: {
|
||||
width: geom::px_to_au(100),
|
||||
height: geom::px_to_au(100)
|
||||
}});
|
||||
}
|
||||
"head" { ret ~es_head; }
|
||||
_ { ret ~es_unknown; }
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
use std;
|
||||
use sdl;
|
||||
use azure;
|
||||
use spidermonkey (name = "js");
|
||||
use js;
|
||||
use harfbuzz;
|
||||
use stb_image;
|
||||
|
||||
|
@ -65,13 +65,10 @@ mod text {
|
|||
|
||||
mod util {
|
||||
mod tree;
|
||||
mod unsafe;
|
||||
}
|
||||
|
||||
mod content {
|
||||
mod js;
|
||||
mod jsglobal;
|
||||
mod jsutil;
|
||||
mod name_pool;
|
||||
}
|
||||
|
||||
mod net {
|
||||
|
|
|
@ -7,12 +7,12 @@ class glyph_pos {
|
|||
|
||||
new(hb_pos: harfbuzz::hb_glyph_position_t) {
|
||||
self.advance = {
|
||||
mut x: px_to_au(hb_pos.x_advance as int),
|
||||
mut y: px_to_au(hb_pos.y_advance as int)
|
||||
x: px_to_au(hb_pos.x_advance as int),
|
||||
y: px_to_au(hb_pos.y_advance as int)
|
||||
};
|
||||
self.offset = {
|
||||
mut x: px_to_au(hb_pos.x_offset as int),
|
||||
mut y: px_to_au(hb_pos.y_offset as int)
|
||||
x: px_to_au(hb_pos.x_offset as int),
|
||||
y: px_to_au(hb_pos.y_offset as int)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
impl methods<T: copy> for *T {
|
||||
unsafe fn +(idx: uint) -> *T {
|
||||
ptr::offset(self, idx)
|
||||
}
|
||||
unsafe fn [](idx: uint) -> T {
|
||||
*(self + idx)
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче