17. c: switch
bytes or tokens
portable
#include <stdio.h>
#include <stdlib.h>
#include "stack.h"
#define READ_OPCODE *PC++
typedef enum {
PUSH = 0,
ADD,
PRINT,
EXIT
} opcodes;
int program [] = {
(int)PUSH, 13,
(int)PUSH, 28,
(int)ADD,
PRINT,
EXIT,
};
STACK *S;
void interpret(int *PC) {
int l, r;
while (1) {
switch(READ_OPCODE) {
case PUSH:
S = push(S, READ_OPCODE);
break;
case ADD:
S = pop(S, &l);
S = pop(S, &r);
S = push(S, l + r);
break;
case PRINT:
printf(“%d + %d = %dn, l, r, S->data);
break;
case EXIT:
return;
}
}
}
int main() {
interpret(program);
}
18. ruby: switch
symbols
class VM
def initialize(*program)
@program = program
@s = []
@pc = 0
end
def interpret
loop do
case read_program
when :push
@s.push(read_program)
when :add
@s[1] += @s[0]
@s = @s.drop(1)
when :print
puts "#{@s[0]}"
when :exit
return
else
puts "#{op.class}"
end
end
end
private def read_program
r = @program[@pc]
@pc += 1
r
end
end
vm = VM.new(
:push, 13,
:push, 28,
:add,
:print,
:exit,
)
vm.interpret
19. c: direct call
pointer to function
multi-byte
portable
#include <stdio.h>
#include <stdlib.h>
#include "stack.h"
#define READ_OPCODE *PC++
typedef void (*opcode)();
STACK *S;
opcode *PC;
void op_add_and_print() {
int l, r;
S = pop(S, &l);
S = pop(S, &r);
S = push(S, l + r);
printf("%d + %d = %dn", l, r, S->data);
}
void op_exit() {
exit(0);
}
opcode program [] = {
op_push, (opcode)(long)13,
op_push, (opcode)(long)28,
op_add_and_print,
op_exit
};
int main() {
PC = program;
while (1) {
(READ_OPCODE)();
}
}
20. ruby: direct call
method names
invoke via send
class VM
def initialize *program
@s = []
@pc = 0
@program = program.collect do |v|
self.respond_to?(v) ? self.method(v) : v
end
end
def interpret
catch :program_complete do
loop do
read_program.call
end
end
end
def push
@s.push(read_program)
end
def exit
throw :program_complete
end
def add_and_print
@s[1] += @s[0]
@s = @s.drop(1)
puts "#{@s[0]}"
end
private def read_program
r = @program[@pc]
@pc += 1
r
end
def dangerous_method
raise "!!! I'M NOT A VALID OP_CODE !!!"
end
end
VM.new(
:push, 13,
:push, 28,
:dangerous_method,
:add_and_print,
:exit
).interpret
21. ruby: direct call
jit compilation
sandboxing
class VM
BLACKLIST = [:load, :compile, :interpret] + Object.methods
def initialize *program
@s = []
load(program)
end
def load program
@program = compile(program)
self
end
def compile program
program.collect do |v|
case
when v.is_a?(Method)
raise "method injection of #{v.inspect} is not supported" if BLACKLIST.include?(v.name)
raise "unknown method #{v.inspect}" unless methods.include?(v.name)
v = v.unbind
v.bind(self)
when methods.include?(v)
raise "direct execution of #{v} is forbidden" if BLACKLIST.include?(v)
self.method(v)
else
v
end
end
end
22. ruby: direct call
jit compilation
sandboxing
def interpret
catch :program_complete do
@pc = 0
loop do
read_program.call
end
end
end
end
begin
VM.new(:dangerous_method)
rescue Exception => e
puts "program compilation failed: #{e}"
end
vm = VM.new
p = vm.compile([
:push, 13,
:push, 28,
:add_and_print,
:exit
])
begin
VM.new(*p)
rescue Exception => e
puts "program compilation failed: #{e}"
end
vm.load(p).interpret
VM.new(*p).interpret
23. c: indirect thread
local jumps
gcc/clang specific
indirect loading
#include <stdio.h>
#include <stdlib.h>
#include "stack.h"
typedef enum {
PUSH = 0, ADD, PRINT, EXIT
} opcodes;
void interpret(int *program) {
static void *opcodes [] = {
&&op_push,
&&op_add,
&&op_print,
&&op_exit
};
int l, r;
STACK *S;
int *PC = program;
goto *opcodes[*PC++];
op_push:
S = push(S, *PC++);
goto *opcodes[*PC++];
op_add:
S = pop(S, &l);
S = pop(S, &r);
S = push(S, l + r);
goto *opcodes[*PC++];
op_print:
printf("%d + %d = %dn", l, r, S->data);
goto *opcodes[*PC++];
op_exit:
return;
}
int main() {
int program [] = {
PUSH, 13,
PUSH, 28,
ADD,
PRINT,
EXIT
};
interpret(program);
}
24. c: direct thread
jit local jumps
gcc/clang specific
direct loading
void interpret(int *PC, int words) {
static void *dispatch_table[] = {
&&op_push,
&&op_add,
&&op_print,
&&op_exit
};
STACK *S;
int l, r;
void **program = compile(PC, words, dispatch_table);
if (program == NULL)
exit(1);
goto **program++;
op_push:
S = push(S, (int)(long)*program++);
goto **program++;
op_add:
S = pop(S, &l);
S = pop(S, &r);
S = push(S, l + r);
goto **program++;
op_print:
printf("%d + %d = %dn", l, r, S->data);
goto **program++;
op_exit:
return;
}
int main() {
int program[] = {
PUSH, 13,
PUSH, 28,
ADD,
PRINT,
EXIT
};
interpret(program, 7);
}
29. vm harness
dispatch
program
program counter
class VM
BL = [:load, :compile, :interpret] + Object.methods
def initialize *program
load(program)
end
def load program
@program = compile(program)
self
end
def interpret
catch :program_complete do
@pc = 0
loop do
read_program.call
end
end
end
def read_program
r = @program[@pc]
@pc += 1
r
end
def compile program
program.collect do |v|
case
when v.is_a?(Method)
if BL.include?(v.name)
raise "forbidden method: #{v.name}"
end
unless methods.include?(v.name)
raise "unknown method: #{v.name}"
end
v = v.unbind
v.bind(self)
when methods.include?(v)
if BL.include?(v)
raise "forbidden method: #{v}"
end
self.method(v)
else
v
end
end
end
end
30. stack machine
zero operands
class Adder < VM
def interpret
@s = []
super
end
def print_state
puts "#{@pc}: @s => #{@s}"
end
def push
@s.push(read_program)
read_program.call
end
def add
@s[1] += @s[0]
@s = @s.drop(1)
read_program.call
end
def exit
throw :program_complete
end
def jump_if_not_zero
if @s[0] == 0
@pc += 1
else
@pc = @program[@pc]
end
read_program.call
end
end
Adder.new(
:push, 13,
:push, -1,
:add,
:print_state,
:read_program,
:print_state,
:jump_if_not_zero, 2,
:exit
).interpret
31. accumulator
machine
single register
single operand
class Adder < VM
def interpret
@s = []
@a = 0
super
end
def print_state
puts "#{@pc}: @a = #{@a}, @s => #{@s}"
end
def clear
@a = 0
read_program.call
end
def push_value
@s.push(read_program)
read_program.call
end
def push
@s.push(@accum)
read_program.call
end
def add
@a += @s.pop
read_program.call
end
def jump_if_not_zero
if @a == 0
@pc += 1
else
@pc = @program[@pc]
end
read_program.call
end
def exit
throw :program_complete
end
end
Adder.new(
:clear,
:push_value, 13,
:print_state,
:add,
:print_state,
:push_value, -1,
:print_state,
:add,
:print_state,
:read_program,
:print_state,
:jump_if_not_zero, 6,
:exit
).interpret
32. register machine
multi-register
multi-operand
class Adder < VM
def interpret
@r = Array.new(2, 0)
super
end
def load_value
@r[read_program] = read_program
read_program.call
end
def add
@r[read_program] += @r[read_program]
read_program.call
end
def jump_if_not_zero
if @r[read_program] == 0
@pc += 1
else
@pc = @program[@pc]
end
read_program.call
end
def exit
throw :program_complete
end
def print_state
puts "#{@pc}: @r => #{@r}"
end
end
Adder.new(
:load_value, 0, 13,
:load_value, 1, -1,
:print_state,
:add, 0, 1,
:print_state,
:jump_if_not_zero, 0, 7,
:read_program,
:print_state,
:exit
).interpret
37. ruby: array heap
core class
s = []
s.push(1)
s.push(3)
puts "depth = #{s.length}"
l = s.pop
r = s.pop
puts "#{l} + #{r} = #{l + r}"
puts "depth = #{s.length}"
38. ruby: c heap
require "fiddle"
class Fiddle::Pointer
NIL = Pointer.new(0)
SIZE = Fixnum::SIZE
PACKING_PATTERN = case SIZE
when 2 then "S"
when 4 then "L"
when 8 then "Q"
end + "!"
def write(value)
str = Fiddle::format(value)
pad = Fiddle::padding(str)
l = pad + str.length
raise BufferOverflow.new(self, l) if l > size
self[0, l] = str + 0.chr * pad
self + l
end
def to_bin
[self].pack(PACKING_PATTERN)
end
end
39. Home (./index.html) Classes
(./index.html#classes) Methods
(./index.html#methods)
In Files
fiddle/closure.c
fiddle/fiddle.c
fiddle/lib/fiddle.rb
fiddle/lib/fiddle/closure.rb
fiddle/lib/fiddle/cparser.rb
fiddle/lib/fiddle/function.rb
fiddle/lib/fiddle/import.rb
fiddle/lib/fiddle/pack.rb
fiddle/lib/fiddle/struct.rb
fiddle/lib/fiddle/types.rb
fiddle/lib/fiddle/value.rb
Namespace
MODULE Fiddle::BasicTypes (Fiddle/BasicTypes.html)
MODULE Fiddle::CParser (Fiddle/CParser.html)
MODULE Fiddle::CStructBuilder (Fiddle/CStructBuilder.html)
MODULE Fiddle::Importer (Fiddle/Importer.html)
MODULE Fiddle::Win32Types (Fiddle/Win32Types.html)
CLASS Fiddle::CStruct (Fiddle/CStruct.html)
CLASS Fiddle::CStructEntity (Fiddle/CStructEntity.html)
CLASS Fiddle::CUnion (Fiddle/CUnion.html)
CLASS Fiddle::CUnionEntity (Fiddle/CUnionEntity.html)
CLASS Fiddle::Closure (Fiddle/Closure.html)
CLASS Fiddle::CompositeHandler (Fiddle/CompositeHandler.html)
Fiddle
A libffi wrapper for Ruby.
Description¶ (#module-Fiddle-label-Description) ↑ (#top)
Fiddle (Fiddle.html) is an extension to translate a foreign function interface (FFI) with
ruby.
It wraps libffi (http://sourceware.org/libffi/), a popular C library which provides a portable
interface that allows code written in one language to call code written in another
language.
Example¶ (#module-Fiddle-label-Example) ↑ (#top)
Here we will use Fiddle::Function (Fiddle/Function.html) to wrap floor(3) from libm
(http://linux.die.net/man/3/floor)
require 'fiddle'
libm = Fiddle.dlopen('/lib/libm.so.6')
floor = Fiddle::Function.new(
libm['floor'],
[Fiddle::TYPE_DOUBLE],
Fiddle::TYPE_DOUBLE
)
access DLLs
call C functions
40. Home (../index.html) Classes
(../index.html#classes) Methods
(../index.html#methods)
In Files
fiddle/closure.c
Parent
Object
Methods
::[] (#method-c-5B-5D)
::malloc (#method-c-malloc)
::new (#method-c-new)
::to_ptr (#method-c-to_ptr)
#+ (#method-i-2B)
#+@ (#method-i-2B-40)
#- (#method-i-2D)
#-@ (#method-i-2D-40)
#<=> (#method-i-3C-3D-3E)
#== (#method-i-3D-3D)
#[] (#method-i-5B-5D)
#[]= (#method-i-5B-5D-3D)
#eql? (#method-i-eql-3F)
#free (#method-i-free)
#free= (#method-i-free-3D)
#inspect (#method-i-inspect)
#null? (#method-i-null-3F)
Fiddle::Pointer
Fiddle::Pointer (Pointer.html) is a class to handle C pointers
Public Class Methods
Get the underlying pointer for ruby object val and return it as a Fiddle::Pointer (Pointer.html) object.
Allocate size bytes of memory and associate it with an optional freefunc that will be called when the
pointer is garbage collected.
freefunc must be an address pointing to a function or an instance of Fiddle::Function (Function.html)
Create a new pointer to address with an optional size and freefunc.
freefunc will be called when the instance is garbage collected.
Fiddle::Pointer[val] => cptr
to_ptr(val) => cptr
Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance
Fiddle::Pointer.new(address) => fiddle_cptr
new(address, size) => fiddle_cptr
new(address, size, freefunc) => fiddle_cptr
MRI stdlib
C pointers
malloc
not portable
41. ruby: c heap
def Fiddle::Pointer.format(value)
value.respond_to?(:to_bin) ? value.to_bin : Marshal.dump(value)
end
42. ruby: c heap
require "fiddle"
class Fiddle::Pointer
NIL = Pointer.new(0)
SIZE = Fixnum::SIZE
PACKING_PATTERN = case SIZE
when 2 then "S"
when 4 then "L"
when 8 then "Q"
end + "!"
def write(value)
str = Fiddle::format(value)
pad = Fiddle::padding(str)
l = pad + str.length
raise BufferOverflow.new(self, l) if l > size
self[0, l] = str + 0.chr * pad
self + l
end
def to_bin
[self].pack(PACKING_PATTERN)
end
end
43. ruby: c heap
class Fixnum
SIZE = 1.size
PACKING_PATTERN = case SIZE
when 2 then "s"
when 4 then "l"
when 8 then "q"
end + "!"
def to_bin
[self].pack(PACKING_PATTERN)
end
def self.read_bin(pointer)
pointer[0, SIZE].unpack(PACKING_PATTERN).first
end
end
44. ruby: c heap
m = Fiddle::Pointer.malloc 64
begin
m.write(0.chr * 59)
m.write(0.chr * 60)
m.write(0.chr * 61)
rescue Fiddle::BufferOverflow => e
p e.message
end
"Buffer overflow: 72 bytes at #<Fiddle::Pointer:0x007f8849052160
ptr=0x007f8849051da0 size=64 free=0x00000000000000>"
45. ruby: c heap
s = "Hello, Terrible Memory Bank!"
i = 4193
f = 17.00091
m.write(i)
puts m.read
q = m.write(-i)
puts m.read
q.write(s)
puts q.read(String)
r = q.write(s[0, s.length - 1])
puts q.read(String)
t = r.write(f)
puts r.read(Float)
t.write(-f)
puts t.read(Float)
=> 4193
=> -4193
=> Hello, Terrible Memory Bank!
=> Hello, Terrible Memory Bank
=> 17.00091
=> -17.00091
52. c: cactus stack
nil is empty
grows on push
manual GC
#include <stdlib.h>
typedef struct stack STACK;
struct stack {
int data;
STACK *next;
};
STACK *push(STACK *s, int data) {
STACK *r = malloc(sizeof(STACK));
r->data = data;
r->next = s;
return r;
}
STACK *pop(STACK *s, int *r) {
if (s == NULL)
exit(1);
*r = s->data;
return s->next;
}
int depth(STACK *s) {
int r = 0;
for (STACK *t = s; t != NULL; t = t->next) {
r++;
}
return r;
}
void gc(STACK **old, int items) {
STACK *t;
for (; items > 0 && *old != NULL; items--) {
t = *old;
*old = (*old)->next;
free(t);
}
}
53. c: cactus stack
nil is empty
grows on push
manual GC
#include <stdio.h>
#include <cactus_stack.h>
int sum(STACK *tos) {
int a = 0;
for (int p = 0; tos != NULL;) {
tos = pop(tos, &p);
a += p;
}
return a;
}
void print_sum(STACK *s) {
printf("%d items: sum = %dn", depth(s), sum(s));
}
int main() {
STACK *s1 = push(NULL, 7);
STACK *s2 = push(push(s1, 7), 11);
s1 = push(push(push(s1, 2), 9), 4);
STACK *s3 = push(s1, 17);
s1 = push(s1, 3);
print_sum(s1);
print_sum(s2);
print_sum(s3);
}
54. ruby: cactus stack
nil is empty
grows on push
automatic GC
class Stack
include Enumerable
attr_reader :head, :tail
def initialize data, tail = nil
@head = data
@tail = tail || EmptyStack.new
end
def push item
Stack.new item, self
end
def pop
[head, tail]
end
def each
t = self
until t.is_a?(EmptyStack)
yield t.head
t = t.tail
end
end
end
class EmptyStack
include Enumerable
def push item
Stack.new item
end
def pop
[nil, self]
end
def each; end
end
60. ruby: hash map
core class
m = {"apple": "rosy"}
puts(m["apple"])
m["blueberry"] = "sweet"
puts m["blueberry"]
m["cherry"] = "pie"
puts m["cherry"]
m["cherry"] = "tart"
puts m["cherry"]
puts m["tart"]
rosy
sweet
pie
tart
61. This repository Pull requests Issu
No description or website provided.
demo + Switched to install for Makefile.
lib Added note about using absolute paths to rootdir rdoc
test Added assertion to quell minitest saying I'm not testing s
Search
seattlerb / rubyinline
Code Issues 0 Pull requests 0 Projects 0 Wiki
176 commits 1 branch
masterBranch: New pull request
zenspider Added note about using absolute paths to rootdir rdoc …
rubyinline
inline c
62. This repository Pull requests Issu
No description or website provided.
lib prepped for release
test + Switched to minitest
History.txt prepped for release
Search
seattlerb / wilson
Code Issues 1 Pull requests 0 Projects 0 Wiki
27 commits 1 branch
masterBranch: New pull request
zenspider prepped for release …
wilson
x86_64 asm
63. This repository Pull requests Issu
NeverSayDie will let you rescue from SEGV's and is evil
ext/neversaydie Changed Config:: for RbCo
lib making class declarations l
test updating rdoc, removing bi
Search
tenderlove / neversaydie
Code Issues 1 Pull requests 1 Projects 0 Wiki
18 commits 1 branch
masterBranch: New pull request
tenderlove Merge pull request #3 from cortexmedia/master …
never day die
libsigsegfault